[Effective C++] 9. 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자
먼저 이 단원에서 얘기하고 싶은 내용을 얘기하자면 객체 생성 및 소멸 과정에서 가상 함수를 호출하면 "미정의 동작"을 할 수 있기 때문에 호출하면 안 된다는 것이다.
이런 현상이 발생하는 원인에 관해서 알아보자.
다형성과 가상 함수
base클래스에서 가상 함수 function()을 선언하고, derived클래스에서 가상 함수 function을 오버라이딩 해준 구조를 생각해보자.
이제 메인에서 base b = new drived(); 이후 b.function(); 호출 시 derived의 함수가 호출되는데, 이게 바로 C++의 다형성의 특징이다.
근데 base의 생성자에서 가상 함수인 function을 호출하게 되면 어떻게 될까?
일단 예제를 확인해보자.
class Transaction
{
public:
Transaction();
virtual void logTransaction() const = 0; // 타입에 따라 달라지는 로그 기록
}
Transaction::Transaction()
{
logTransaction(); // 기본 클래스의 생성자에서 가상 함수 호출..!!
}
class BuyTransaction: public Transaction // Transaction의 파생클래스
{
public:
virtual void logTrnasaction() const;
}
int main()
{
BuyTansaction b;
return 0;
}
메인을 보면 BuyTransaction의 생성자가 호출되는 것은 맞다. 그러나 이 전에 Transaction의 생성자가 먼저 호출됨을 알 수 있는데 여기서 logTransation은 Transaction의 함수이다.
그 이유는, 기본 클래스 생성자가 돌아가고 있을 때 파생 클래스는 초기화 이전 상태이기 때문에 기본 클래스의 함수를 호출하게 되며 '미정의 동작'이 일어나게 된다.
물론, 생성자뿐만 아닌 소멸자도 동일한 문제가 발생한다.
해결 방법
여러 가지 방법을 찾을 수 있는데, 그중 한 가지를 소개하자면 가상 함수가 아닌 비가상 멤버 함수를 사용하는 것이다.
위의 예제 같은 경우 타입에 따라 달라지는 로그 기록을 노출하고 싶으니, 파생 클래스의 생성자들로 하여금 필요한 로그 정보를 Transaction의 생성자로 넘기고 logTransaction을 비가상 함수로 변경하면 BuyTransaction객체의 초기화되지 않은 멤버를 건드릴 위험이 없기에 안전해진다.
class Transaction
{
public:
explicit Transaction(const std::string& logInfo); // explicit 자신이 원하지 않은 형변환이 일어나지 않도록 제한
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);
}
요약
- 생성자 혹은 소멸자 안에서 가상 함수를 호출하면 안 된다. 가상 함수라고 해도, 지금 실행 중인 생성자나 소멸자에 해당되는 클래스의 파생 클래스 쪽으로 내려가지 않기 때문이다.
'Books > Effective C++' 카테고리의 다른 글
[Effective C++] 11. operator=에서는 자기 대입에 대한 처리가 빠지지 않도록 하자 (0) | 2022.05.18 |
---|---|
[Effective C++] 10. 대입 연산자는 *this의 참조자를 반환하게 하자 (0) | 2022.05.14 |
[Effective C++] 8. 예외가 소멸자를 떠나지 못하도록 붙들어 놓자 (0) | 2022.05.14 |
[Effective C++] 7. 다형성을 가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자 (0) | 2022.05.13 |
[Effective C++] 6. 컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해 버리자 (0) | 2022.05.09 |
댓글
이 글 공유하기
다른 글
-
[Effective C++] 11. operator=에서는 자기 대입에 대한 처리가 빠지지 않도록 하자
[Effective C++] 11. operator=에서는 자기 대입에 대한 처리가 빠지지 않도록 하자
2022.05.18 -
[Effective C++] 10. 대입 연산자는 *this의 참조자를 반환하게 하자
[Effective C++] 10. 대입 연산자는 *this의 참조자를 반환하게 하자
2022.05.14 -
[Effective C++] 8. 예외가 소멸자를 떠나지 못하도록 붙들어 놓자
[Effective C++] 8. 예외가 소멸자를 떠나지 못하도록 붙들어 놓자
2022.05.14 -
[Effective C++] 7. 다형성을 가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자
[Effective C++] 7. 다형성을 가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자
2022.05.13