페이지

2013년 6월 22일 토요일

C++ 생성자 함수에서 virtual 함수를 호출 할 수 있을까?

이 글은 c++에서 왜 생성자 함수내에서 가상함수 호출을 하면 안되는가 대한 이유를 설명하는 글입니다. 아래 링크의 내용을 참고하였으나 제 마음대로 해석한 내용이므로 원본의 문장과는 다를 수 있습니다.

http://www.stroustrup.com/bs_faq2.html#vcall


답은 호출 할 수 있다. 하지만 사용하는데 조심해야 한다.
예상치 못한 일이 발생 할 수 있기 때문이다. 생성자 함수 내에서 가상함수를 호출하는 메카니즘은 사실 무의미하다. 왜냐면 파생클래스로부터의 오버라이딩이 아직 발생하지 않았기 때문이다. 모든 객체들은 "base before derived" 라는 기본 개념을 기반으로 설계 되어진다.

아래 예제 코드를 보자.

 #include<string>
 #include<iostream>
 using namespace std;

 class B {
 public:
  B(const string& ss) { cout << "B constructor\n"; f(ss); }
  virtual void f(const string&) { cout << "B::f\n";}
 };

 class D : public B {
 public:
  D(const string & ss) :B(ss) { cout << "D constructor\n";}
  void f(const string& ss) { cout << "D::f\n"; s = ss; }
 private:
  string s;
 };

 int main()
 {
  D d("Hello");
 }
 
출력 결과:
 B constructor
 B::f
 D constructor

D::f 가 아니다!! 어찌보면 너무 당연해보이지만.. 만약 B::B()로부터 D::f()가 호출되기 위해서 그 규칙이 다르다면 어떨지 생각해봐라. D::D() 생성자는 아직 실행되기도 전이기 때문에 D::f() 함수는 초기화 되지도 않은 string s를 할당하려고 할 것이다. 그 결과는 거의 곧 바로 크래쉬가 날 것이 뻔하다.

소멸자는 "derived class before base class" 을 기반으로 한다. 그래서 가상함수들은 생성자함수 내에서 처럼 동작한다. 오직 로컬 정의들만 사용되어진다. 그리고 현재 지워진 파생된 클래스를 건드리지 않기 위해 호출을 하지 않는다.

그동안 이것은 구현상의 제약사항으로 제안되어져 왔다. 하지만 그렇지 않다. 실제론 일반 함수내에서 가상함수를 호출하는 것처럼 생성자에서 호출하는 것은 안전하지 않은 규칙으로 여기는 것이 더 쉬울것이다.

댓글 없음:

댓글 쓰기