エンジニアのソフトウェア的愛情

または私は如何にして心配するのを止めてプログラムを・愛する・ようになったか

コンストラクタ/デストラクタ内での仮想関数の振る舞い

デストラクタからオーバーライドされた仮想関数が呼べない - imHo

リンク先のページにもあるように、C++ではでは構築時には先に派生元が構築され、破棄時には派生元が後に破棄されます。つまり、派生元のコンストラクタ/デストラクタが動いているときは派生先のオブジェクトはまだ生成されてないあるいはすでに破棄された状態になっています。このため派生元のクラスのコンストラクタ/デストラクタから派生先の仮想関数を呼び出そうとしても呼び出せないようになっています。

#include <iostream>

class Base
{
public:
    Base()
    {
        std::cout << "Base::Base" << std::endl;
        construct();
    }

    virtual ~Base()
    {
        std::cout << "Base::~Base" << std::endl;
        destruct();
    }

    virtual void construct()
    {
        std::cout << "Base::construct" << std::endl;
    }

    virtual void destruct()
    {
        std::cout << "Base::destruct" << std::endl;
    }
};

class Derived : public Base
{
public:
    Derived()
    {
        std::cout << "Derived::Derived" << std::endl;
        construct();
    }

    ~Derived()
    {
        std::cout << "Derived::~Derived" << std::endl;
        destruct();
    }

    void construct()
    {
        std::cout << "Derived::construct" << std::endl;
    }

    void destruct()
    {
        std::cout << "Derived::destruct" << std::endl;
    }
};

int main(int, char*[])
{
	Base* p = new Base;
	delete p;

	std::cout << "--" << std::endl;

	p = new Derived;
	delete p;

	return 0;
}
Base::Base
Base::construct
Base::~Base
Base::destruct
  • -
Base::Base Base::construct Derived::Derived Derived::construct Derived::~Derived Derived::destruct Base::~Base Base::destruct


逆にJavaでは派生先のコンストラクタが先に呼び出されるため、派生元のコンストラクタから派生先の関数を呼び出すことができます。
(※「派生先のコンストラクタが先に呼び出される」は記憶違いの可能性。勉強し直します)

class Base
{
    Base()
    {
        System.out.println("Base::Base");
        construct();
    }

    void construct()
    {
        System.out.println("Base::construct");
    }
}

class Derived extends Base
{
    Derived()
    {
        super();
        System.out.println("Derived::Derived");
        construct();
    }

    void construct()
    {
        System.out.println("Derived::construct");
    }
}

public class ConstDestSample
{
    public static void main(String[] args)
    {
        Base p = new Base();

        System.out.println("--");

        p = new Derived();
    }
}
Base::Base
Base::construct
  • -
Base::Base Derived::construct Derived::Derived Derived::construct


余談なんですが、DelphiJavaと同じように派生先のコンストラクタが先に呼び出されるしくみになっていて、DelphiC++を混在させて使うときの注意点としてこれらのことが記述されていて、それでこのことを学んだ記憶があります。