私が理解しているようvirtual
に、オブジェクト内の関数ポインター テーブルの場所はコンパイラに依存します。
このポインターをオブジェクトの先頭と末尾、またはその逆に配置することの長所/短所はありますか?
2 に答える
仮想関数テーブルの単なる存在はコンパイラに依存し (ただし、すべてのコンパイラがそうです)、場所も義務付けられていません...詳細を知っているすべてのコンパイラで、vptr はオブジェクトの先頭に格納されます。その理由は、均一な場所を提供するためです。クラス階層を考えてみましょう:
struct base {
T data;
virtual void f();
};
struct derived : base {
T1 data;
virtual void g();
};
vptr がオブジェクトの最後に格納された場合、それsizeof(T)
は完全な型のオブジェクトのバイトの後になりますbase
。完全なタイプのオブジェクトがある場合derived
、base
サブオブジェクトのレイアウトは完全なオブジェクトのレイアウトと互換性がある必要があるbase
ため、vptr
はsizeof(T)
オブジェクト内のバイトである必要があり、derived
オブジェクトの途中にあります (sizeof(T)
最初からsizeof(T1)
最後まで)。そのため、オブジェクトの最後ではなくなります。
さらに、this
ポインタを指定すると、仮想呼び出しには vtable を介した間接参照が必要になります。これは、基本的に を逆参照しvptr
、オフセットを追加し、そこに格納されているメモリ位置にジャンプします。がオブジェクトの末尾に格納されている場合、vptr
仮想呼び出しごとに、 をthis
逆参照する前に に追加の追加が行われvptr
ます。
はい、完全に実装依存です。
単純な継承階層の場合、オブジェクトの先頭に配置されますが、複雑な階層の場合はそうではありません。
とにかく、作成するソース コードは、その場所に依存するべきではありません。実際、作成するコードは、仮想テーブルまたは仮想テーブル ポインターの存在に依存するべきではありません。
C++ 標準では、仮想テーブルとポインターを介して仮想ディスパッチを実装することは義務付けられていません。実装は、他の実装方法を使用して自由に実装できます。ただし、すべての主流のコンパイラは、テーブル ポインター メカニズムを介してこれを実装します。ポインターが配置されている場所などの正確な実装。