자, 이부분도 상당히 충격을 주었던 부분중 하나인데 우선 제목 그대로 객체의 생성자와 소멸자에서는 절대로 가상 함수를 호출 해서는 안된다. 우선 아래 코드를 보자.
class Creature
{
public:
Creature();
virtual void logLife() const = 0; // 타입에 따른 수명을 출력함.
...
};
Creature::Creature()
{
...
logLife(); // 여러가지 작업을 한 후 마지막으로 생명체의 수명을 로깅함.
}
class Dog : public Creature // 파생 클래스
{
public:
virtual void logLife() const; // 오버 라이딩
...
};
class Cat : public Creature // 파생 클래스
{
public:
virtual void logLife() const; // 오버 라이딩
...
};
자 위와 같이 Creature라는 기본 클래스를 만들고 가상함수로 생성자 호출시 마지막 부분에서 파생클래스의 수명을 로깅하도록 생성자를 작성했다. 우리의 생각대로라면 Dog나 Cat을 생성 하였을때 각각 정해진 수명이 출력 되어야 할 것이다. 하지만 현실은 그렇지 않다. 일단 Dog 객체를 생성했다고 치자, 그러면 우선 Dog의 생성자가 호출이 될것이다. 그러면 Dog의 생성자는 호출되는 즉시 부모 객체인 Creature의 생성자를 호출한다. 그러면 Creature의 생성자에서는 logLife라는 가상함수를 호출하려고 할 것이다. 하지만 이때 logLife는 Creature에서 호출이 되고 있다. 즉 소유권이 Dog의 것이 아니라 Creature의 logLife를 호출한다는 말이다. 그러니 정말 어처구니 없는 상황이 발생하게 된다. 그래서 이것을 해결하기 위해 혹여나 dynamic_cast나 typeid를 사용하여 타입을 변환하여 호출 시키는 방법을 구현하고자 하려 들어도 C++에서 객체가 생성되는 동안에는 무슨 짓을 하더라도 항상 그 객체의 기본 클래스 타입이 된다는 것이다. 그러니 이것을 자기 고집대로 해결하려 들지 말고 어짜피 C++에서 가능할리 없으니 생성자에서는 가상함수를 호출하지 말도록 하자.
다음 이야기로 넘어가서 이번에는 소멸자에 관해서 생각해보자. 사실 소멸자도 생성자와 똑같다. 일단 소멸자가 호출되고 나면 파생 객체의 데이터 멤버는 정의되지 않은 값으로 가정되고 기본 클래스 소멸자에 진입하면 그 객체는 이제 파생 객체가 아니 기본 클래스 객체가 되어 버린다. 자 그렇다면 어떻게 이 문제를 해결 할 수 있을까?
방법은 간단하다. 바로 비가상 함수를 이용하는 법이다. 하지만 기억해야 할것은 파생 객체의 생성자가 호출되어도 기본 클래스 타입으로 여겨진다는 점이다. 그러면 이를 참고하여 아래 코드를 보자.
class Creature
{
public:
Creature(cosnt std::string& logInfo);
void logLife(cosnt std::string& logInfo) const; // 매개변수를 통해 출력을 달리함
...
};
Creature::Creature(const std::string& logInfo)
{
...
logLife(logInfo); // 여러가지 작업을 한 후 마지막으로 생명체의 수명을 로깅함.
}
class Dog : public Creature // 파생 클래스
{
public:
Dog( parameters )
: Creature(createLogString(parameters))
{ ... }
...
private:
static std::string createLogString(parameters);
};
자 다음과 같이 매개변수를 이용하는 것이다. 파생 객체의 매개변수로 부터 로깅에 출력할 텍스트를 생성하는 함수를 만들고 이를 이용하여 기본 클래스의 생성자의 매개변수에 넘겨주어 비가상 함수를 이용해 출력을 하는것이다. 다소 복잡한 구조 이지만 다양한 상황에서 충분히 응용가능할 것이다. 아무튼 이번 항목에서는 생성자와 소멸자에서는 가상함수를 호출해서는 안된다는 것을 꼭 기억하도록 하자.
'C++ > Effective C++' 카테고리의 다른 글
항목 13 자원 관리에는 객체가 그만! (2) | 2024.01.19 |
---|---|
항목 11 operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자 (0) | 2024.01.18 |
항목 8 예외가 소멸자를 떠나지 못하도록 붙들어 놓자 (0) | 2024.01.18 |
항목 7 다형성을 가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자 (0) | 2024.01.18 |
항목 6 컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해 버리자 (2) | 2024.01.18 |