항목 9 : 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자
객체 생성 및 소멸 과정 중에는 가상 함수를 호출하면 절대로 안됩니다!
기본 클래스의 생성자가 호출될 도안에는, 가상 함수는 절대로 파생 클래스 쪽으로 내려가지 않습니다. 그 대신, 객체 자신이 기본 클래스 타입인 것처럼 동작합니다,
즉, 기본 클래스 생성 과정에서는 가상 함수가 먹히지 않습니다.
이 같은 동작에는 다 이유가 있습니다.
아시다시피 기본 클래스 생성자는 파생 클래스 생성자보다 앞서서 실행되기 때문에, 기본 클래스 생성자가 돌아가고 있을 시점에 파생 클래스 데이터 멤버는 아직 초기화된 상태가 아니라는 것이 핵심입니다.
이때 기본 클래스 생성자에서 어쩌다 호출된 가상 함수가 파생 클래스 쪽으로 내려간다면 어떻게 될까요? 파생 클래스 버전의 가상 함수는 파생 클래스만의 데이터 멤버를 건드릴 것이 뻔한데, 이들은 아직 초기화되지 않았습니다.
이러한 상황을 방지하기 위해 C++은 이런 실수조차 하지 못하도록 막은 것입니다.
객체가 소멸될 때는 어떻게 될까요?
파생 클래스의 솔멸자가 일단 호출되고 나면 파생 클래스만의 데이터 멤버는 정의되지 않은 값으로 가정하기 때문에, 이제부터 C++는 이들을 없는 것처럼 취급하고 진행합니다. 기본 클래스 소멸자에 진입할 당시의 객체는 더도 덜도 아닌 기본 클래스 객체가 되며, 모든 C++ 기능들 역시 기본 클래스 객체의 자격으로 처리합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class Transaction {
public :
Transaction()
{ init(); } // 비가상 멤버 함수를 호출하고 있...
virtual void logTransaction() const = 0;
...
private :
void init()
{
...
logTransaction(); // ...는데 이 비가상 함수에서 가상 함수를 호출하고 있어요!
}
};
Colored by Color Scripter
|
이런 코드의 문제점을 해결하는 방법은 여러가지가 있지만 그 중 한가지는
logTransaction을 Transaction 클래스의 비가상 멤버 함수로 바꾸는 것입니다. 그러고 나서 파생 클래스의 생성자들로 하여금 필요한 로그 정보를 Transaction의 생성자로 넘겨야 한다는 규칙을 만듭니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
class Transaction {
public :
explicit Transaction(const std :: string& logInfo);
virtual 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 );
};
Colored by Color Scripter
|
코드를 이렇게 작성할 경우 기본 클래스 부분이 생성될 때는 가상 함수를 호출한다 해도 기본 클래스의 울타리를 넘어 내려갈 수 없기 때문에, 필요한 초기화 정보를 파생 클래스 쪽에서 기본 클래스 생성자로 '올려'주도록 만듦으로써 부족한 부분을 역으로 채울 수 있다는 것입니다.
꼭 잊지 말아야 할 것!
생성자 혹은 소멸자 안에서 가상 함수를 호출하지 마세요. 가상 함수라고 해도, 지금 실행중인 생성자나 소멸자에 해당되는 클래스의 파생 클래스 쪽으로는 내려가지 않으니까요.
'언어 > C++' 카테고리의 다른 글
[Effective C++] operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자 (0) | 2020.01.11 |
---|---|
[Effective C++] 대입 연산자는 *this의 참조자를 반환하게 하자 (0) | 2020.01.10 |
[Effective C++] 예외가 소멸자를 떠나지 못하도록 붙들어 놓자 (0) | 2020.01.08 |
[Effective C++] 다형성을 가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자 (0) | 2020.01.07 |
[Effective C++] 컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해 버리자 (0) | 2020.01.06 |