C++の仮想テーブルの仮想ポインタ(VPTR)のサイズは? また、これは宿題の質問ではありません... C ++の本を読んでいるときに頭に浮かんだ質問です。
6 に答える
このトピックに関連する優れた記事は、Member Function Pointers and the Fastest possible C++ Delegatesです。この記事では、さまざまなコンパイラのメンバー関数ポインターの実装について深く掘り下げます。この記事では、特に多重 (および仮想) 継承に照らして、vtable ポインターのすべてのニュアンスについて説明します。
多重継承を適切に処理するために、オブジェクトに複数の VPTR が存在する可能性がありますが、一般に、それぞれが単純なアーキテクチャ依存のポインターである可能性が高いことに注意してください。
次のようなものを実行して、コンパイラがどのようにレイアウトするかを確認してください。
#include <iostream>
using namespace std;
struct Base
{
int B;
virtual ~Base() {}
};
struct Base2
{
int B2;
virtual ~Base2() {}
};
struct Derived : public Base, public Base2
{
int D;
};
int main(int argc, char* argv[])
{
cout << "Base:" << sizeof (Base) << endl;
cout << "Base2:" << sizeof (Base2) << endl;
cout << "Derived:" << sizeof (Derived) << endl;
Derived *d = new Derived();
cout << d << endl;
cout << static_cast<Base*>(d) << endl;
cout << &(d->B) << endl;
cout << static_cast<Base2*>(d) << endl;
cout << &(d->B2) << endl;
cout << &(d->D) << endl;
delete d;
return 0;
}
私の 32 ビット コンパイラでは、両方の基本クラスに 8 バイト、派生クラスに 20 バイトが与えられます (64 ビット用にコンパイルすると、これらの値が 2 倍になります)。
4 bytes Derived/Base VPTR
4 bytes int B
4 bytes Derived/Base2 VPTR
4 bytes int B2
4 bytes int D
最初の 8 バイトを見ると Derived を Base として扱うことができ、2 番目の 8 バイトを見るとそれを Base2 として扱うことができることがわかります。
それは実装によって異なりますが、簡単に見つけることができます。このプログラムの場合
#include <iostream>
struct virtual_base {
int data;
virtual_base() {}
virtual ~virtual_base() {}
};
struct non_virtual_base {
int data;
non_virtual_base() {}
~non_virtual_base() {}
};
int main() {
std::cout << sizeof( virtual_base ) - sizeof( non_virtual_base ) << '\n';
return 0;
}
mine(VC 2008)は4を出力するので、この場合、ポリモーフィズムのコストは4バイトです。
ほとんどの場合、他のポインターのサイズです。コンパイラとマシンを調べるには、次のようなことを試してください。
#include <iostream>
struct base {
base() {}
virtual ~base() {}
};
int main( int argc, char **argv ) {
std::cout << sizeof( base ) << std::endl;
}
通常、仮想関数テーブル内のポインターは、システム内の通常のポインターと同じサイズです。通常、仮想関数テーブルはすべての型に対して計算され、各オブジェクト インスタンスにはその型のテーブルへのポインターが含まれるため、仮想関数を含むオブジェクトのインスタンスは、sizeof(void *)
そうでないものよりもインスタンスごとに多くのバイトを使用します。複数の基本型から派生する型は、任意の基本型にキャスト可能である必要があるため、必要に応じて基本型の仮想関数テーブルへの複数のポインターを含めることができます。もちろん、これはすべてコンパイラに依存します。
おそらく通常のポインターと同じサイズです...通常、32ビットマシンでは4バイトです。ただし、これはコンパイラに依存するため、一部のコンパイラは異なる処理を行う場合があります。