프로그래밍 언어/C/C++

constructor/destructor에서 virtual method의 호출

소혼 2013. 2. 27. 22:39
반응형

virtual키워드를 사용해 child 객체의 메소드들을 호출할 수 있다는 것은 C++을 공부한 사람이라면 다들 알고 있는 사항이다.

예를 들어, 아래의 코드에서는 child의 call method가 호출된다.

### c++

class parent {

public: 

    virtual void call();

};

class child : public parent {

public:

    void call();

};

int main()

{

    parent* p = new child;

    p->call(); // child의 call이 호출됨

}



그렇다면 child의 instance는 virtual로 선언된 메소드들에 대해서 항상 child를 호출하는가?

오늘 그렇지 않다는 것을 배웠다.


아래 예제를 보자.

### c++

#include <stdio.h>

static void externalCall();

class A
{
public:
    A() {
        printf("In Constructor A\n");
        call();
    }
    virtual ~A() {
        printf("In Destructor A\n");
        call();
        externalCall();
    }
    virtual void call() {
        printf("    Call A is called\n");
    }
    void callFromA() {
        printf(" > In %s \n", __func__);
        call();
    }
};

class B : public A
{
public:
    B() {
        printf("In Constructor B\n");
        call();
    }
    ~B() {
        printf("In Destructor B\n");
        call();
        externalCall();
    }
    void call() {
        printf("    Call B is called\n");
    }
};

static A* global;

static void externalCall()
{
    printf(" > In %s \n", __func__);
    global->call();
}

int main()
{
    A* local = new B;
    printf("In %s \n", __func__);
    local->call();
    local->callFromA();

    global = local;
    externalCall();

    delete local;
}



위 예제를 실행해보면 재밌는 결과를 얻을 수 있다.

In Constructor A ---- 1
    Call A is called
In Constructor B
    Call B is called
In main  ---- 2
    Call B is called
 > In callFromA
    Call B is called
 > In externalCall
    Call B is called
In Destructor B ---- 3
    Call B is called
 > In externalCall
    Call B is called
In Destructor A
    Call A is called
 > In externalCall
    Call A is called


1. 실행 결과에서 먼저 생성자의 호출 순서를 알 수 있다.

생성자의 경우 부모클래스(A)의 생성자가 호출된 후, 자식 클래스(B)의 생성자가 호출된다.

이 때, 자식 클래스의 생성자가 호출되기 이전에 부른 call method는 부모 클래스의 method가 호출됨을 알 수 있다.


2. main에서는 예측한 것과 같이 자식 클래스(B)의 method만이 호출됨을 알 수 있다.

설령, 부모클래스의 메소드를 거쳐서 호출하더라도(callFromA) 자식 클래스의 인스턴스이기 때문에 자식 클래스의 메소드가 호출된다.


3. destructor의 호출 순서는 생성자의 경우와 반대로 자식 클래스의 소멸자 -> 부모클래스의 소멸자 순으로 불린다.

이 때, 자식 클래스의 소멸자 안에서 호출한 call method는 자식 클래스(B)의 메소드가 불린 반면,

부모 클래스(A)의 소멸자 안에서는 자식 클래스(B)의 call method가 아닌, 부모 클래스(A)의 call method가 불린 것을 알 수 있다.


externalCall의 결과만 보면 자식 클래스(B)의 소멸자에서 불릴때와 부모클래스(A)의 소멸자에서 불릴때 다른 메소드가 불리게 된다는 것을 알 수 있다.

소멸자가 부른 외부 함수(또는 다른 클래스의 함수)가 이를 모르고 호출할 경우, 의도하지 않은 동작을 할 수도 있기 때문에,

소멸자가 호출하는 함수가 다시 자신의 메소드를 호출하지 않도록 주의할 필요가 있을 것 같다.


PS>

쓰고 보니 이미 Effective C++에서 다루고 있는 내용이었다.

다시 읽어봐야겠다. ㅠ_ㅠ


http://www.artima.com/cppsource/nevercall.html

반응형

'프로그래밍 언어 > C/C++' 카테고리의 다른 글

[C++11] Type Inference: auto  (4) 2013.11.01
c++11 스터디(목차)  (0) 2013.11.01
GCC 옵션 : version script  (0) 2012.08.14
[C++] new는 null을 return하는가?  (1) 2012.01.17
static const char* vs static const char []  (0) 2012.01.04