5

大規模な C++ ソフトウェア設計(Lakos)、652 ページから:

問題は、「コンパイラーが特定のクラスの仮想テーブル定義をどの固有の変換単位に置くか」です。CFRONT (およびその他の多くの C++ 実装) で採用されているトリックは、外部仮想テーブルを変換単位に配置することです。変換単位は、クラスに現れる字句的に最初の非インライン関数 (存在する場合) を定義します。

これは、最も使用されているコンパイラ (GCC および Visual C++) にも当てはまりますか? それとも今まででしたか?

4

1 に答える 1

7

GCC はたまたま、質問 ( http://gcc.gnu.org/onlinedocs/gcc/Vague-Linkage.html )で説明されているように動作することを文書化しています。

VTable

C++ 仮想関数は、vtable と呼ばれるルックアップ テーブルを使用してほとんどのコンパイラに実装されます。vtable には、クラスによって提供される仮想関数へのポインターが含まれ、クラスの各オブジェクトには、その vtable (複数の継承がある状況では vtable) へのポインターが含まれます。クラスが非インライン、非純粋仮想関数を宣言する場合、最初の関数がクラスの「キー メソッド」として選択され、vtable はキー メソッドが定義されている変換単位でのみ生成されます。

注: 選択したキー メソッドが後でインラインとして定義された場合でも、vtable はそれを定義するすべての翻訳単位で出力されます。インライン仮想が定義されていなくても、クラス本体でインラインで宣言されていることを確認してください。

ただし、複数のオブジェクト ファイルにわたって複数の vtable が存在する可能性がある状況でも (「キー メソッド」がインラインであることが判明した場合に発生する可能性があります)、コンパイラは可能であれば重複を無視するように調整しますが、重複が終了する可能性があります。ターゲットが COMDAT をサポートしていない場合、最終バイナリでスペースを使用します。

GNU/Linux や Solaris 2 などの ELF システム、または Microsoft Windows で GNU ld バージョン 2.8 以降を使用すると、これらの構造の重複コピーはリンク時に破棄されます。これは、COMDAT サポートと呼ばれます。

COMDAT をサポートしていないが、弱いシンボルをサポートしているターゲットでは、GCC はそれらを使用します。この方法では、1 つのコピーが他のすべてのコピーを上書きしますが、未使用のコピーは実行可能ファイルのスペースを占有します。

COMDAT も弱いシンボルもサポートしていないターゲットの場合、あいまいなリンケージを持つほとんどのエンティティは、リンカーからの重複定義エラーを回避するためにローカル シンボルとして出力されます。ただし、複数のコピーがあるとほぼ確実に問題が発生するため、これはインラインのローカル スタティックでは発生しません。

__ZTVFWIW、GCCは vtable にで始まるシンボルを使用しているようです。

MSVC に関する限り、VC++10 でのいくつかの経験的テスト (MS が動作を文書化しているとは思わない) は、VC が vtable を単一のオブジェクト ファイルに制限しようとしていないように見えることを示しています。Microsoft は、COMDAT セクションをサポートするリンカーに依存できることを知っており、コンストラクターは vtable を直接使用する唯一の関数であるため (他のすべての vtable の使用はオブジェクト ポインターを介して間接的に行われると思います)、VC は単にコピーを配置するように見えます。コンストラクターがインスタンス化される任意のオブジェクト ファイル内の vtable。コンパイラによって生成された ctor を使用するクラスの場合、それはその型のオブジェクトが構築される場所です。

于 2012-08-19T10:27:36.517 に答える