3

Visual Studio 2008、c++、win32、ドットネットなしを使用しています。

親のデストラクタで呼び出される仮想関数に問題があります。たとえば、クラス a と b を取り上げます。クラス a には、Hello() という仮想関数があります。呼び出されると、hello が出力されます。

クラス b はクラス a を継承し、関数 Hello も実装します。呼び出されると、world が出力されます。

クラス a のデストラクタでは、関数 Hello が呼び出されます。クラス b が削除されると、まず b のデストラクタが呼び出され、次に a のデストラクタが呼び出されます。a のデストラクタでは、関数 Hello が呼び出されますが、それはクラス b ではなくクラス a に関連付けられた実装です。

この例のコードは、この行の下にあります。

class a{
public:
  virtual void Hello();
  a();
  ~a();
};

a::a(){
};

a::~a(){
  Hello();
}

void a::Hello(){
  printf("hello\n");
}

class b:public a{
public:
  virtual void Hello();
  b();
  ~b();  
};

b::b(){
}

b::~b(){
}

void b::Hello(){
  printf("world\n");
}

int _tmain(int argc, _TCHAR* argv[]){  
  a* exampleA=new a();
  b* exampleB=new b(); 
  exampleA->Hello();
  exampleB->Hello();
  delete exampleA;
  delete exampleB;
  return 0;
}

出力は次のとおりです。

hello
world
hello
hello

クラス b のデストラクタにブレークポイントを配置すると、ローカル変数の __vfptr は次のようになります。

__vfptr
  [0x0]    0x002314ce b::Hello(void)

ステップスルーしてクラス a のデストラクタに入ると、ローカル変数の __vfptr が次のように変更されます。

__vfptr
  [0x0]   0x0024c864 a::Hello(void)

これは普通ですか?そして、クラス a のデストラクタ内で Hello の b の実装を呼び出す (正しい) 方法はありませんか?

4

3 に答える 3

1

12.7/4によると、

仮想関数がコンストラクタまたはデストラクタから直接または間接的に呼び出される場合、[...] 呼び出される関数は、コンストラクタまたはデストラクタのクラスの最終オーバーライドであり、より派生したクラスでそれをオーバーライドする関数ではありません。

したがって、動作は明確に定義されており、クラス自体でメンバー関数を呼び出します。動的ディスパッチはありません。

于 2013-01-24T14:29:56.857 に答える
1

そのとおりです。Helloのデストラクタ内でb する方法はありませんa。同じことがコンストラクタにも当てはまります。

破壊時に多態的に呼び出されるものが本当に必要な場合は、プライベート/保護されたデストラクタと、最初に仮想関数を呼び出してから自己破壊するパブリックの destroy メソッドを使用して、2 段階の破壊を強制できます。

于 2013-01-24T14:24:46.057 に答える