밑의 코드를 봐보자
class Transaction //모든 거래에 대한
{ //기본 클래스
public:
Transaction();
virtual void logTransaction() const = 0; //타입에 따라 달라지는
//로그 기록을 만든다.
....;
};
Transaction::Transaction() //기본 클래스 생성자의 구현
{
...;
logTransaction(); //마지막 동작으로, 이 거래를
} //로깅을 구현한다.
class BuyTransaction : public Transaction //Transaction 파생 클래스
{
public:
virtual void logTransaction() const; //이 타입에 따른 거래내역
....; //로깅을 구현한다.
};
class SellTransaction : public Transaction //역시 파생 클래스
{
public:
virtual void logTransaction() const; //이 타입에 따른 거래 내역
}; //로깅을 구현한다.
BuyTransaction b;
이 코드를 보면 기본 클래스 생성자에서 가상 함수를 호출하고 있다. 이러면 큰일 난다. 왜? 한번 생각해보자 우리가 파생 클래스를 만들면 파생 클래스를 바로 초기화하는 것이 아니고 기본 클래스를 먼저 초기화한다는 것이다. C++의 배려로 우리가 초기화 안된 파생 클래스의 가상 함수 호출 대신 지금 생성자가 호출되는 클래스의 가상 함수를 사용한다.(더 자세한 동작 과정은 _vfptr의 동작을 잘 생각해봐야행 ㅇㅇ..) 소멸자도 마찬가지 ㅇㅇ
아 그리고 당연하지만 아래의 코드도 하면 안 된다.
class Transaction
{
public:
Transaction();
{init(); } //비 가상멤버 함수를 호출하고 있...
virtual void logTransaction() const = 0;
.....;
private:
void init()
{
logTransaction(); //...는데 이 비가상 함수에서
//가상 함수를 호출하고 있어!
}
};
하지만 이렇게 생성자에 가상함수를 넣은 것 같은 효과를 볼 수 있는 방법이 있다. logTransaction을 비 가상 함수로 만들어 필요한 초기화 정보를 파생 클래스에서 기본 클래스로 올려주는 것이다.
class Transaction //모든 거래에 대한
{ //기본 클래스
public:
explicit Transaction(const std::string& logInfo);
void logTransaction(const std::string& logInfo) const; //이제는 비가상
//함수이다
....;
};
Transaction::Transaction(const std::string& logInfo)
{
logTransaction(logInfo);
}
class BuyTransaction : public Transaction //Transaction 파생 클래스
{
private:
static std::string CreateLogString(parameters);
public:
BuyTransaction(parameters) //로그 정보를
: Transaction(CreateLogString(parameters)) {} //기본 클래스
....; //생성자로 넘긴다.
};
BuyTransaction b;
이것만은 잊지 말자!
1. 생성자 혹은 소멸자 안에서 가상 함수를 호출하지 말자. 가상 함수라고 해도, 지금 실행 중인 생성자나 소멸자에 해당되는 클래스의 파생 클래스 쪽으로는 내려가지 않으니까