C++ 프로그래밍을 하다 보면 간혹 이와 같은 희귀한 오류 메시지를 만나게 되는 경우가 있습니다. 보통 다음과 같은 양상으로 나타나게 되는데요.
pure virtual method called
terminate called without an active exception
이런 오류 메시지가 나타나는 원인은 이렇습니다. 다음 코드를 보시죠.
class A {
public :
A() {}
void init() {
....
virtual_method();
....
}
virtual void virtual_method() = 0;
};
class B : public A {
public :
B() {}
void virtual_method() {
using namespace std;
cout << "this is the implementation of pure virtual function A::virtual_method()\n";
}
};
어디서 많이 보던 패턴이죠? ^^ 상위 클래스에는 알고리즘의 골격만 들어가고, 하위 클래스에는 해당 알고리즘을 구성하는 부분 알고리즘(위의 경우에는 virtual_method())의 실제 구현이 들어가는 그런 구조입니다. 알고리즘의 전체 골격은 바뀌지 않으면서, 그 세부사항만 하위 클래스에 따라 달라질 때 유용한 구조입니다.
그런데 이런 식으로 하는 것 자체에는 그다지 문제가 없는데, 문제는 init()을 호출하는 위치를 A 클래스의 생성자 안으로 이동시키게 되는 순간 발생합니다.
class A {
void init() {
....
virtual_method();
....
}
public :
A() {
init();
}
virtual void virtual_method() = 0;
};
겉보기에는 꽤나 그럴싸하죠. B bb; 하는 순간 B 객체에 포함된 A 부분이 생성될 테고, A 부분은 자신의 멤버 변수 초기화를 마친 다음에 init() 함수를 불러서 알고리즘을 구동시키고, 그러면 그 알고리즘의 일부로 B 클래스에 정의된 멤버 함수가 자동적으로 호출되어 객체 전반적인 초기화를 완료하는...
그런데 이런 식의 초기화는 잘 될 턱이 없습니다. A가 private 함수 init()을 호출하는 순간 B::virtual_method가 호출되는데, 아직 객체 bb의 B 부분은 초기화가 되지도 않았거든요. -_- 그러니 B::virtual_method가 온전히 동작할 리가 없습니다. 사실 호출되는 것 자체가 불가능하죠. 객체 bb의 B 부분이 초기화되지 않았으니, 가상함수 테이블도 만들어지지 않았을 테니 말이에요.
그러니 위의 코드는 컴파일은 잘 됩니다만 (그러니 프로그래머는 '모든 순수 가상함수들을 구현했다니깐!'하는 식으로 착각하게 됩니다) 실제로 돌려보면 런타임에는 해당 가상함수의 구현이 발견되지 않습니다. 그래서 컴파일 시간이 아닌 실행 시간에
pure virtual method called
terminate called without an active exception
이런 괴상망칙한 오류가 발생하게 되는거죠. 나는 분명 구현했는데 그 함수가 아직도 '순수 가상 함수라니...' 정말 짜증나는 상황이지 않나요? ㅋㅋ
Effective Java라는 책에서도 지적되었던 것 같습니다만, 이런 이유로 해서 상위 클래스의 '생성자'안에서 하위 클래스의 메소드를 호출하는 것은 불가능하거나, 가능하더라도 피해야만 합니다. 설사 저런 오류가 나지 않더라도, 초기화되지 않은 메모리에다가 뭔 짓을 하려고 하면 그게 제대로 될 리가 없거든요.
- - -
이 글을 쓰고 웹 서핑을 하다보니 Meyer 선생도 자신의 저서(Effective C++ 3rd Edition, Item9)에서 같은 문제를 지적했더군요.
Never call virtual functions during construction or destruction.
이 문제에 대한 좀 더 상세한 지식을 얻고 싶으시다면, 여기에 가보시는 것도 좋겠습니다. :-)
'Languages > C++' 카테고리의 다른 글
| VI와 ctags (0) | 2007/12/18 |
|---|---|
| empty container의 반환 (0) | 2007/12/14 |
| 메모리 할당과 초기화는 다르다 (2) | 2007/12/14 |
| Destructor가 왜 2번 불리는 거죠? (0) | 2007/12/12 |
| pure virtual method called (2) | 2007/12/06 |
| stringstream (2) | 2007/12/04 |
댓글을 달아 주세요
클래스 A 에서 void virtual_method() = 0; 가 virtual void virtual_method() = 0; 로 바뀌어야 하지 않나요?
2009/06/12 10:24 [ ADDR : EDIT/ DEL : REPLY ]문제 상황을 재현해 보려다가 안되길래 글 남깁니다.
맞습니다. ^^ 빠뜨렸네요. 지적 감사해요~
2009/06/12 10:39 [ ADDR : EDIT/ DEL ]