방프리

17.07.07. Effective C++ 2. 생성자, 소멸자 및 대입 연산자 (항목9) 본문

C++/Effective C++

17.07.07. Effective C++ 2. 생성자, 소멸자 및 대입 연산자 (항목9)

방프리 2020. 1. 6. 01:45

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

 

객체 지향에 들어서면서 가장 중요해진 것이 함수 호출의 순서입니다. C언어의 경우 특징이 절차지향이기 때문에

순서대로 가기에 상관이 없었지만 C++에 들어와서는 이야기가 달라졌죠

이번 항목도 앞서 말한 것과 관련이 깊은 내용이라고 생각합니다.

간단하게 예를 들어보겠습니다.

class Person // 사람 관련 클래스

{

public:

Person();

virtual void Action() const = 0; // 행동에 관한 함수

};

class Student : public Person

{

public:

Student()

{

Action(); // 객체 생성자 안에서 가상 함수를 호출합니다.

}

};

 

자 이 예제에서 Student 생성자 안에서 호출된 가상함수 Action은 누가 소유하고 있는 Action일까요? 

보통 이렇게 선언한다면 개발자 생각에는 Student 클래스가 가지고 있는 Action 함수를 호출하기 위해

이렇게 만들었을 것입니다.

하지말 불행히도 Student가 가지고 있는 함수가 아닌 Person이 소유하고 있는 Action 함수가 호출됩니다. 

왜 이렇게 동작하게 되는 걸까요? 일단 첫 번째로 우리가 놓치고 있는 사실은 부모 클래스의 생성자는

자식 클래스의 생성자보다 더 빨리 생성됩니다.

즉, 자식 클래스의 생성자가 호출되고 실행되면 부모 클래스의 생성자는 이미 생성하고

기다리고 있다는 것입니다. 

두 번째로는 Student 클래스는 초기화 및 할당 조차 되지 않았습니다.

Action 함수를 불러들이기엔 너무 이르다는 것이죠.

사실 말로는 부모클래스의 Action함수가 실행된다고 했지만 마냥 그렇지만은 않습니다.

어떤 오류를 내뿜을지 아무도 모르죠

즉, 언제 터져서 디버깅 모드로 들어갈지 아무도 모른다는 것입니다.

이와 같은 내용은 소멸자에서도 마찬가지로 적용됩니다. 

그럼 진정으로 해결방법은 없을까요?? 바로 가상함수로 선언된 함수를 비가상함수로 전환하는 것입니다.

 

class Person

{

public :

explicit Person(const std::string& logInfo)

{

...

Action(logInfo);

}

 

void Action(const std::string& logInfo) const;

};

 

class Student : public Person

{

public:

Student( parameters )

:Person( parameters )

{

};

};

 

이렇게 만든다면 Action 함수는 기본 클래스 범위를 벗어나지 못하기 때문에 안전하게 실행됩니다. 

 

이것만은 잊지말자!

* 생성자 혹은 소멸자 안에서 가상 함수를 호출하지 마세요 가상 함수라고 해도, 지금 실행 중인 생성자나 소멸자에 해당되는 클래스의 파생 클래스 쪽으로는 내려가지 않으니깐요.

Comments