2

C ++では、コンストラクタ/デストラクタ仮想関数を呼び出さないでください。

これらの関数をクラススコープで明示的に呼び出すとどうなりますか?

基本的に、私たちは仮想性から利益を得たくないと言っています。

以下の擬似コードは正しいですか?

 struct CA{ 
    CA(){ CA::foo();} // calling foo() without CA is wrong
    virtual int foo(){} 
 };

 struct CB{ 
    CB(){ CB::foo();} // calling foo() without CB is wrong
    virtual int foo(); 
 }
4

2 に答える 2

1
struct CA{ 
   CA(){ CA::foo(); } // calling foo() without CA is wrong
   virtual int foo(){}
};

struct CB { 
   CB(){ CB::foo(); } // calling foo() without CB is wrong
   virtual int foo(); 
};

プログラム内で非純粋仮想関数を定義する必要があり、CB::fooどこかに定義が必要であり、クラス定義内で定義されているかどうかは実際には問題ではないことを考えると、2つのクラスの間に顕著な違いは見られません。何処か別の場所。

foo()どちらの場合も(明示的な修飾なしで)への呼び出しは間違っています。foo()とを使用する場合の違いは、最初のケースでは最終的に関数の最終オーバーライドType::foo()を呼び出す動的ディスパッチを使用しているに対し、修飾されたケース ( ) では追加の修飾によって動的ディスパッチが禁止されることです。Type::foo

この時点でに注意することが重要です。オブジェクトの動的な型は、型階層の構築中に変更されます。基本コンストラクターを評価している間、動的型はbaseであるため、最終的なオーバーライドはそのクラスからのみ取得できます。基本コンストラクターが完了すると、動的型は階層内の次の型に変更され、最終的なオーバーライドはこれら 2 つの型から選択できます。

struct base {
   base() { foo() };
   virtual void foo() { std::cout << "base\n"; }
};
struct d1 : base {
   d1() : base() { foo() };
};
struct d2 : d1 {
   d2() : d1() { foo() };
   virtual void foo() { std::cout << "d2\n"; }
};

型のオブジェクトの構築は、サブオブジェクトd2を構築することから始まります。そこでの呼び出しは、現時点では型のみを考慮して出力する最終的なオーバーライドにディスパッチされます。次に、コンストラクターが実行され、このレベルで最終的なオーバーライドを選択する呼び出しが行われます。その後、コンストラクターが評価されます。この時点で、最終的なオーバーライダーが出力され、次の結果が得られます。basefoo()base"base"d1foo()base::foo()d2d2::foo()"d2"

base
base
d2

私たちが作成している完全なオブジェクトだけがタイプであると考えたとしてもd2、それは一時的にbaseオブジェクトとして動作し、次にオブジェクトとして動作し、コンストラクターが実行を開始しd1たときにのみ、オブジェクトとして動作し始め、 を呼び出すことに注意してください。これは多くの人にとって混乱を招くと考えられています。d2d2d2

各レベルでのオブジェクトの動的な型は、評価されるコンストラクターの型とまったく同じであるため、最終的なオーバーライドは常にその型にディスパッチされることに注意してください。余分な資格を追加しても、対処する場合を除いて大きな違いはありません...

純粋仮想関数

純粋仮想関数は定義を持つことができます。純粋修飾子は、派生型がその仮想関数の定義を提供する必要があることを意味し、動的ディスパッチが実行されるときに純粋仮想関数 (定義されている場合) が呼び出されないことも意味します。その特定のケースでは、動的ディスパッチを無効にする追加の修飾を使用して、その特定の実装を呼び出すことができます。

struct base {
   base() { base::foo(); }  // [*]
   virtual void foo() = 0;
};
void base::foo() { std::cout << "base\n"; }
struct derived : base {
   derived() : base() { foo(); base::foo(); }
   virtual void foo() { std::cout << "derived\n"; }
};

これは、評価されるコンストラクターが純粋な仮想であり、最終的な overriderfooがない場合など、 inへの非修飾呼び出しの形式が正しくない唯一のケースです。一方、修飾を追加すると、動的ディスパッチが無効になり、上記のコードがコンパイルおよび実行されます。また、コンストラクターでは、追加の修飾を使用して、このレベルで最終的なオーバーライドではない可能性がある関数の特定の定義を選択できることに注意してください。上記のコードは次を出力します。[*]fooderivedbase::foo

base
derived
base
于 2013-03-07T14:18:24.907 に答える
1

呼び出しCB::foo()は動的にディスパッチされないため、あなたやこのコードを読んでいる人を待っている不幸な驚きはありません。全然大丈夫です。実際、単に を呼び出すだけでも問題foo()ありません。これは、このシナリオでは標準で同じであるためです。ただし、同僚にコメントを残したい場合もあります。

于 2013-03-07T13:34:44.083 に答える