これを見てください:
#include <iostream>
class Base
{
public:
void nonvirtualmethod()
{ std::cout << "Base nonvirtualmethod" << std::endl; }
virtual void virtualmethod()
{ std::cout << "Base virtualmethod" << std::endl; }
};
class Derived: public Base
{
public:
void nonvirtualmethod()
{ std::cout << "Derived nonvirtualmethod" << std::endl; }
virtual void virtualmethod()
{ std::cout << "Derived virtualmethod" << std::endl; }
};
int main()
{
Derived d;
Derived* pd = &d;
Base* pb = &d; //< NOTE: both pd and pb point to the same object
pd->nonvirtualmethod();
pb->nonvirtualmethod();
pd->virtualmethod();
pb->virtualmethod();
}
次の出力を提供します。
Derived nonvirtualmethod
Base nonvirtualmethod
Derived virtualmethod
Derived virtualmethod //< invoked by a Base*
pb
これは、ポインタの静的型(Base*
)とポインタが指す動的型()に違いがあるためDerived
です。仮想メソッドとプレーンメソッドの違いは、非仮想メソッドは静的型マッピングに従う(つまり、Base
ポインターはBase::
メソッドを呼び出す)のに対し、仮想メソッドは実行時型のチェーンに従うため、をBase*
指す場合Derived
、Derived
メソッドが呼び出されることです。 。
この意味で、デストラクタは特別なものではありません。仮想でない場合、Base
ポインタはデストラクタを呼び出さないためDerived
、メモリストアに戻される、半分破壊されたオブジェクトが残ります。
これがUBである(そして単に否定されていない)理由は、「メモリストア」が言語自体によって管理されていないためですが、プログラムがホストされているプラットフォームからです。クラッシュは、そのDerived
部分(まだ生きている)は、オペレーティングシステムが間違った開始アドレスでメモリのブロックを解放しようとする結果になります。