正確に。一部のクラスには仮想メソッドがないため、vtable がありません。
仮想メソッドは、クラスの実装によって異なるため、コンパイラが直接呼び出しを生成できないメソッドです。Vtable は一種のルックアップ テーブルで、プログラムの実行時にどの実装を呼び出すかの決定を遅らせることで、この問題を解決します。関数呼び出しを生成する代わりに、コンパイラは vtable でメソッド ルックアップを生成し、返されたメソッドを呼び出します。 .
次の例を見てください。
class Foo
{
public:
virtual void vMethod()
{
std::cout << "Foo::vMethod was called!" << std::endl;
}
};
class Bar : public Foo
{
public:
virtual void vMethod()
{
std::cout << "Bar::vMethod was called!" << std::endl;
std::cout << "This is not the same as Foo::vMethod." << std::endl;
}
};
Foo* foo = new Bar;
foo->vMethod();
これにより、Bar
のメッセージが出力されます。ほとんどの重要なシナリオでは、コンパイラは、仮想メソッドが呼び出されるオブジェクトの型を事前に知ることができません。前述のように、vtable は、オブジェクトのタイプに関係なく、メソッドの実装を見つけるための統一されたルックアップ メカニズムを提供することで問題を解決します。
vtable ポインターは、クラスのすべてのインスタンスに存在する必要があります (これには、追加のメモリのポインターのサイズが必要です。おそらく 4 または 8 バイトです)。これは大したことではないように思えるかもしれませんが (実際、多くの人が同意するでしょう)、これは特定のシナリオ (メモリが非常に限られている組み込みシステムなど) では扱いにくい場合があります。クラスごとに vtable を用意すると、使用した分だけ料金を支払うという一般的な C++ の原則に違反するため、必要がなければコンパイラは vtable を生成しません。
vtableがない場合、実行時の型情報が無効になるという顕著な副作用があります。コードでRTTIを使用する必要がある場合、クラスには少なくとも 1 つの仮想メソッドが必要です。このような場合、デストラクタを virtual とマークするのが慣習です。