純粋仮想メソッドのみで構成されるクラスを宣言するC++ヘッダーがあります。そのヘッダーを使用する2つのDLLがあります(1つはそのインターフェイスを実装しています)が、コンパイル時にリンクされていません。一方のDLLは、もう一方のDLLを動的にロードし、実装されたインターフェイスのポインタをもう一方に渡します。これらのDLLは同じ仮想テーブル構造を共有していますか?
4 に答える
もちろん、クラスヘッダーは、正確な仮想テーブル構造を含む完全なクラス(ここでは、メモリ内のレイアウト、すべてがどのように配置されているか、実際のデータではない)を構築するのに十分です。
考えてみてください。各リンクオブジェクト(.cppファイル)は個別にコンパイルされ、共通のヘッダーファイルのみが含まれますが、コンパイル時に、コンパイラは仮想呼び出しを正しくルーティングするために仮想テーブルの正確な構造を知る必要があります。
ちなみに読み通す質問のDoozy...
私が正しく理解していれば、あなたは次のようなものを持っています:
ああ
class A
{
public:
virtual void foo()=0;
}
B.cpp
#include <A.h>
class B : public A
{
public:
void foo()
{}
}
C.cpp
#include <A.h>
void bar(A * a)
{}
したがって、をB.cpp
実装するクラスがあり、 (のインスタンスで提供する)のインスタンスへのポインタを受け入れる関数があります。あれは正しいですか?A
C.cpp
A
B
もしそうなら、はい、これらはvtableを共有します。vtableは、クラスをコンパイルすることによって作成され、独自のvtableはまったくB
ありません。C.cpp
あなたは安全です。
メソッドがに表示される順序はvftable
、基本クラスの構造によって決まります。注意する必要があるのはそれだけです。ただし、これはコンパイラ固有であるため、dllの生成には同じコンパイラを使用してください。それらが下位互換性があることに依存しないでください(または少なくともドキュメントを確認してください)。
次のヘッダーがあると仮定します。
//header.h
class A
{
public:
virtual void foo() = 0;
virtual void goo() = 0;
};
そして、あなたはB.dll
次のクラスを持っています:
class B : public A
{
public:
virtual void foo() {}
virtual void goo() {}
}
ここで、で、で作成されたオブジェクトであるX.dll
へのポインタを受け取ります。A
B
B.dll
呼び出し
void test( A* a )
{
a->foo();
}
を呼び出しB::foo()
ます。
試してみることができる巧妙な実験の1つは、コンパイルすることです。コンパイルB.dll
するheader.h
ときはX.dll
、次のメソッドの順序を逆にしheader.h
ます。
//header.h
class A
{
public:
virtual void goo() = 0;
virtual void foo() = 0;
};
この場合、これを行うべきではありませんが、test()
inへの同じ呼び出しX.dll
でメソッドが呼び出される可能性がありますB::goo()
。これは、ヘッダーX.dll
に存在することを前提としているためです。vftable
ただし、これは未定義の動作です。この例を書いたのは、要点を説明するためです。
これはすべてコンパイラに依存しますが、一般に、クラスが純粋な仮想であり、変換ユニットでメンバー関数が定義されていない場合、コンパイラは弱いシンボルvtable
としてを生成します。あなたの特定のケースでは、異なる翻訳単位は基本タイプに対して別々の正確に等しいsを生成し、リンカー/ローダーは他の弱いシンボルと同じように1つを除くすべてのシンボルを破棄します(テンプレート化された非インライン関数を考えてください) 。vtable
ただし、の生成はvtable
標準化されていないことに注意してください。つまり、2つの異なるコンパイラまたはバージョンのコードを混在させると、ODR違反が発生する可能性があります。