본문 바로가기
책 정리/Effective C++ 3rd

항목 9. 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자

by ocean20 2019. 12. 17.

1. 다형성과 가상함수

C++ 다형성을 생각해보자.

base 클래스에서 가상함수인 function() 선언해줬고

derived 클래스에서 가상함수 function() 오버라이딩해주었다.

 

이제 메인에서 base b = new derived(); 이후

b.function(); 호출 derived 함수가 호출된다.

이것이 바로 c++ 다형성 특징이다.

 

근데 만약, base 생성자에서 가상함수인 function() 호출하게 되면 "미정의 동작행"으로 빠질 있다.

일단 예제 코드를 확인해보자.

//기본클래스
class Transaction {
public :
  Transaction();
  virtual void logTransaction() const = 0;
  ...
};

Transaction::Transaction()()
{
  ...
  logTransaction(); // 기본클래스의 생성자에서 가상함수를 호출하였다.. 문제됨
}

//파생클래스1
class BuyTransaction: public Transaction{
public:
  virtual void logTransaction() const;
  ...
};

문제점  : 메인에서 BuyTransaction b; 을 선언시, BuyTransaction 생성자가 호출되기전에 Transaction 생성자가 먼저 호출된다. 따라서 logTransaction(); 호출되는데 이때 함수의 타입자격은 BuyTransaction 아니라 Transaction이라는 것이다. 그 이유는, 기본 클래스 생성자가 돌아가고 있을때, 파생클래스는 초기화 이전 상태이기 때문이다.

만약, logTransaction() 호출될때 초기화도 안된 파생클래스를 건드린다면.. 재앙일것이다. 소멸될때에도 동일하다.

 

2. 해결방법

  - logTransaction Transaction 비가상 멤버함수로 변경한다.

  - 이후 파생클래스의 생성자들로 하여금 필요한 정보를 기본클래스인 Transaction 생성자로 넘긴다.

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{
public:
    BuyTransaction(parameters) :Transaction(createLogString(parameters)){}  //생성자에서 파라미터를 기본생성자로 넘긴다.
private:
    static std::string createLogString(parameters); // 밑에서 내용확인해보자.
};

createLogString 정적함수를 살펴보자.  함수는 기본클래스 생성자쪽으로 데이터를 넘기는 도우미 함수역할을 한다.

예제코드를 보면 생성이 채끝나지 않은 BuyTransaction객체의 미초기화된 데이터 멤버를 건드릴 위험도 없다.

왜냐면 정적함수는 정적멤버변수만 사용하기때문이다.

 

이것만은 잊지 말자!

  - 생성자 or 소멸자에서 가상함수를 호출하지 말자. 가상함수라고 해도, 실행중인 생성자 or 소멸자에 해당하는 클래스로 내려가진 않으니...

댓글