1 minute read

class Base
{
public:
    Base()
    {
        WriteLine("Base::Base()", typeid(*this).name());
        f();
    }
    void f()
    {
        WriteLine("Base::f()", typeid(*this).name());
        virt();
    }
protected:
    virtual void virt() { WriteLine("Base::virt()"); }
};

class Derived : public Base
{
public:
    Derived()
    {
        WriteLine("Derived::Derived()", typeid(*this).name());
        f();
    }

protected:
    virtual void virt() { WriteLine("Derived::virt()"); }
};

// main
Derived d;
Base::Base() - class Base
Base::f() - class Base
Base::virt()
Derived::Derived() - class Derived
Base::f() - class Derived
Derived::virt()

pure virtual call 문제가 생기는 이유를 알아보기에 앞서 먼저 생성자에서 가상 함수를 호출할 때 어떻게 동작하는지 살펴봐야 한다. 3번째 줄을 눈여겨 보자. 왜 Derived::virt() 가 아니라 Base::virt() 가 호출됐을까?

원인은 c++에서 최종 타입이 되는 시기가 상식적이지 않기 때문이다. Derived 오브젝트를 생성할 때, 부모가 있기 때문에 Base::Base() 를 먼저 호출하게 되는데, 이때 타입은 Base이다. 그래서 Derived::virt() 가 아니라 Base::virt() 가 호출됐다. 그럼 언제 Derived 타입이 되는가? Base::Base() 호출이 끝나고 Derived::Derived() 가 호출될 때, 그제야 Derived 타입이 된다.

//virtual void virt()   { WriteLine("Base::virt()"); }
virtual void virt() = 0;
R6025 - pure virtual function call

자, 이제 Base 클래스에 있는 virt() 함수를 pure virtual 함수로 바꿔보자. 위에서 봤던 대로 생성자에서 가상 함수를 호출할 때(거쳐서 호출), Base:virt() 가 호출된다. Base:virt() 는 pure virtual function. 짠~ 그래서 프로그램이 실패하게 되는 것.

이런 언어 특성 때문에 Mock Object를 만들 때 많이 사용하는 Extract and override factory method 리팩토링 방법을 쓰지 못하고 Abstract Factory pattern과 같은 다른 방법을 사용해야 한다.

참고