デストラクタからオーバーライドされた仮想関数が呼べない - 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
- -
逆に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
- -
余談なんですが、DelphiもJavaと同じように派生先のコンストラクタが先に呼び出されるしくみになっていて、DelphiとC++を混在させて使うときの注意点としてこれらのことが記述されていて、それでこのことを学んだ記憶があります。