15

次の点を考慮してください。

Xhで:

class X
{
    X();
    virtual ~X();
};

X.cpp:

#include "X.h"

X::X()
{}

これをビルドしてみてください (欠落しているメインのエラーを回避するために .dll ターゲットを使用しており、Visual Studio 2010 を使用しています):

エラー 1 エラー LNK2001: 未解決の外部シンボル "private: virtual __thiscall X::~X(void)" (??1X@@EAE@XZ)

ただし、わずかな変更でビルドは成功します。

じ:

class X
{
    inline X(); // Now inlined, and everything builds
    virtual ~X();
};

また

じ:

class X
{
    X();
    ~X(); // No longer virtual, and everything builds
};

.dtor が仮想の場合、または .ctor がインライン化されていない場合、リンカで未解決の外部が発生する原因は何ですか?

編集:

または、おそらくもっと興味深いことに、デストラクタを非仮想にした場合、またはコンストラクタをインライン化した場合、未解決の外部が得られないのはなぜですか?

4

7 に答える 7

23

状況 1:

コンストラクターのコードがあります。
そのため、コンストラクターをオブジェクト ファイルに組み込みます。コンストラクターは、コンストラクターを構築できないため、仮想テーブルに配置するデストラクタのアドレスが必要です。

状況 2: (インライン コンストラクター)

コンパイラーは、コンストラクターをビルドする必要がないと判断します (インライン化されるため)。
そのため、コードを植えないため、デストラクタのアドレスは必要ありません。

タイプ X のオブジェクトをインスタンス化すると、再びエラーが発生します。

状況 3: (非仮想デストラクタ)

コンストラクタを構築するためにデストラクタのアドレスは必要ありません。
だから文句は言わない。

タイプ X のオブジェクトをインスタンス化すると、エラーが発生します。

于 2010-08-24T20:54:07.050 に答える
7

仮想デストラクタにボディを与える必要があります。


class X
{
    X();
    virtual ~X() {}
};
于 2010-08-24T20:37:43.203 に答える
5

C++ では、関数をプログラムで使用する場合にのみ定義する必要があります (3.2/2 の ODR を参照)。一般に、非仮想関数は、評価される可能性のある式から呼び出される場合に使用されます。非純粋仮想関数は、無条件に使用されていると見なされます。[非仮想] 特別なメンバー関数が使用される場合、言語標準の専用の場所で定義されます。等々。

  • 最初の例では、デストラクタを非純粋仮想関数として宣言しました。これは、デストラクタがプログラムで使用されていることを即座に意味します。これは、そのデストラクタの定義が必要であることを意味します。定義を提供できなかったため、コンパイラはエラーを報告しました。

  • 3番目の例では、デストラクタは非仮想です。プログラムでデストラクタを使用していないため、定義は必要なく、コードはコンパイルされます (デストラクタの使用を構成するものの詳細な説明については、12.4 を参照してください)。

  • 2番目の例では、コンストラクターがインライン化されているという事実によってトリガーされる、実装の癖を扱っています。デストラクタは非純仮想であるため、定義が必要です。ただし、コンパイラはエラーを検出できませんでした。これが、コードが正常にコンパイルされたように見える理由です。実装の詳細でこの動作の理由を掘り下げることができますが、C++ の観点からは、この例はまったく同じ理由で最初の例と同じように壊れています。

于 2010-08-24T21:06:17.933 に答える
2

あなたの最初の質問への答え、

.dtorが仮想である場合、または.ctorがインライン化されていない場合、リンカーの外部が未解決になる原因は何ですか?

...簡単に言うと、デストラクタの定義がないということです。

今、あなたの2番目の質問はもう少し興味深いです:

デストラクタを非仮想化した場合、またはコンストラクタをインライン化した場合に、未解決の外部を取得しないのはなぜですか?

その理由は、Xインスタンス化したことがないため、コンパイラがのデストラクタを必要としなかったためX、クラス全体が破棄されたためです。このプログラムをコンパイルしようとすると、未解決の外部が取得されます。

class X
{
public:
    X();
     ~X();
};

X::X() {};

int main()
{
    X x;
    return 0;
}

しかし、あなたがコメントアウトするX x;と、あなたが観察したように、それはうまくコンパイルされます。

ここで、デストラクタの場合にコンパイルされない理由に戻りましょうvirtual。ここで推測していますが、その理由は、仮想デストラクタがあるため、Xポリモーフィッククラスになっているためだと思います。ポリモーフィッククラスをメモリにレイアウトするために、vtableを使用してポリモーフィズムを実装するコンパイラには、すべての仮想関数のアドレスが必要です。を実装していないX::~Xため、未解決の外部結果が発生します。

ポリモーフィッククラスではXなかったときのように、コンパイラが単に破棄しないのはなぜですか?Xここでより多くの憶測。ただし、その理由は、直接インスタンス化していない場合でも、コード内のどこにも他の何かになりすましてライブをX実行していないことを確認できないためだと思います。X例として、抽象基本クラスについて考えてみます。Baseこの場合、直接インスタンス化することは決してなく、のコードDerivedは完全に別個の変換ユニットにある可能性があります。したがって、コンパイラがこのポリモーフィッククラスに到達すると、インスタンス化したことを知らなくても、コンパイラはそれを破棄できません。

于 2010-08-24T21:04:01.510 に答える
1

これは実装定義の動作であるという疑いがあります。これが理由です

$ 10.3/8-「クラスで宣言された仮想関数は、そのクラスで定義されるか、純粋に宣言されるか (10.4)、またはその両方が必要ですが、診断は必要ありません (3.2)。」

GCC は以下のようなエラーを返しますが、これも (少なくとも私にとっては) 仮想関数の実装の非標準実装の詳細について非常に示唆的です。

/home/OyXDcE/ccS7g3Vl.o: X::X()': prog.cpp:(.text+0x6): undefined reference toX' の関数 vtable 内 /home/OyXDcE/ccS7g3Vl.o: X' collect2 の関数 X::X()': prog.cpp:(.text+0x16): undefined reference tovtable 内: ld は 1 終了ステータスを返しました

OPコードのコンパイラからの診断が本当に必要かどうか混乱しているので、反対票を投じるリスクがあるとしても、これを投稿することを考えました:)。もちろん、良いコンパイラだと思います。

于 2010-08-25T02:41:04.387 に答える
1

これらはまだ完全なプログラムではありません (完全な DLL でさえありません)。X は ~X() の定義なしでは使用できないため、エラーが発生した場合は実際に助けられています。

つまり、この特定のコンパイラ インスタンスには、場合によってはその定義が必要だったということです。コンパイルしても何もしません。

于 2010-08-24T20:38:18.710 に答える
0

constr と destr の両方がプライベートであるため、これを回避している可能性があります。ビルドにクラス X への参照が他にない場合、コンパイラは destr が不要であると推測している可能性があるため、定義の欠如は大したことではありません。

これは、ケース 1 が失敗し、2 と 3 が正常にビルドされる理由を説明していません。両方が公開されたらどうなるのだろうか?

于 2010-08-24T20:53:26.867 に答える