1. 팩토리함수
- 정의 : (생성된 파생클래스 객체)를 가리키는 (기본클래스의 포인터)를 반환하는 함수
- 유의할점 : 파생클래스의 객체를 new로 할당하기때문에 메모리를 적절히 삭제해야한다.
* 문제가 되는 코드를 살펴보자.
class product{
public:
virtual void print() = 0;
};
class concreteProduct : product{
public:
void print() override{ cout<< "concreteProduct"<<endl; }
};
class Creator{
public:
Product* AnOperation() { return factoryMethod(); }
protected:
virtual Product* factoryMethod() = 0 ;
};
class ConcreteCreator : Creator{
private:
Product* factoryMethod() { return new ConcreteProduct;}
};
int main()
{
ConcreteCreator* cc ;
Product* product = cc.AnOperation(); // 기본클래스product 포인터가 파생클래스 ConcreteProduct객체를 가리킨다
product->print();
delete product; // 기본클래스를 통해 파생클래스의 메모리를 삭제한다.
}
* 풀이
- main함수의 2번째줄을 보면 (기본클래스의 포인터)가 (파생클래스의 객체)를 가리킨다.
- main함수의 4번째줄 처럼, 기본클래스의 포인터를 사용하여 파생클래스 객체메모리를 해제한다면, 오류가 발생한다. (c++ 규정이 그러하다.)
- 오류내용 : 기본클래스에 가상소멸자가 없을경우, 다형성의원리로 인해, 파생객체의 메모리가 해제 되지않는다. 파생클래스로 만들어진 객체의 소멸자가 호출 되지 않음
2. 결론
- 기본클래스(ex. Product)의 소멸자를 가상으로 선언하여 위 문제를 해결한다.
- 기본클래스가 가상멤버함수를 하나라도 가지고 있다면, 가상소멸자도 가져야한다.
3. 의문점
- 질문 : 만약, 기본클래스(추상화클래스)역할을 수행하지않는 부모클래스(가상함수가 없는클래스)에서 소멸자를 가상으로 선언할경우에는 어떨까?
- 답변 : 가상소멸자를 선언해준 경우, 해당함수를 호출하기위해서 Vptr이라는 녀석이 필요한데 이녀석도 포인터이기에 32비트 시스템에서 4바이트를 차지한다. 따라서 다른언어로 만들어진 소스의 해당 객체를 넘길시 vptr도 같이넘어가기때문에 이식성은 포기해야한다.
4. 정리
- 가상소멸자는 해당클래스가 가상함수를 가질때만 사용하자.
5. 추상클래스에서 순수가상함수를 쓰지 않을 경우
- 이 경우, 순수 가상소멸자를 선언 및 정의를 해줘야한다. --> 왜? 파생클래스가 임무를 마치고 메모리 해제 될때, 해제순서가 파생클래스 ->기본클래스 순이다. 기본클래스의 가상소멸자가 선언만 있고 정의는 없을경우, 호출이 불가능하므로 , 선언 그리고 정의도 해주어야 한다.
이것만은 잊지말자 !
- 다형성을 가진 기본 클래스에는 반드시 가상 소멸자를 선언해야 한다. 즉, 어떤 클래스가 가상함수를 하나라도 갖고 있으면, 이클래스의 소멸자도 가상 소멸자이어야 한다.
- 기본 클래스로 설계되지 않았거나 다형성을 갖도록 설계되지 않은 클래스에는 가상 소멸자를 선언하지 말아야 한다.
'책 정리 > Effective C++ 3rd' 카테고리의 다른 글
항목 4. 객체를 사용하기 전에 반드시 그 객체를 초기화하자 (0) | 2019.12.15 |
---|---|
항목 3. 낌새만 보이면 const를 들이대 보자! (0) | 2019.12.10 |
항목 2. #define을 쓰려거든 const, enum, inline을 떠올리자 (0) | 2019.12.04 |
항목 6. 컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해버리자 (0) | 2019.10.23 |
항목 5. C++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자 (0) | 2019.10.23 |
댓글