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 소멸자에 해당하는 클래스로 내려가진 않으니...
'책 정리 > Effective C++ 3rd' 카테고리의 다른 글
항목 11. operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자 (0) | 2019.12.18 |
---|---|
항목 10. 대입 연산자는 *this의 참조자를 반환하게 하자 (0) | 2019.12.18 |
항목 8. 예외가 소멸자를 떠나지 못하도록 붙들어 놓자 (0) | 2019.12.17 |
항목 4. 객체를 사용하기 전에 반드시 그 객체를 초기화하자 (0) | 2019.12.15 |
항목 3. 낌새만 보이면 const를 들이대 보자! (0) | 2019.12.10 |
댓글