2
#include <iostream>
using namespace std;

class C
{
public:
    virtual void a();
};

class D : public C
{
public:
    void a() { cout<<"D::a\n"; }
    void b() { cout<<"D::b\n"; }
};

int main()
{
    D a;
    a.b();

    return 0;
}

に関するリンク エラーが発生していundefined reference to 'vtable for C'ます。これはどういう意味で、なぜですか?

問題は明らかに、基本クラスに定義されていない非純粋仮想関数があることですが、呼び出していない場合、なぜこれがリンカを悩ませるのでしょうか? 私が宣言して定義していない他の関数と違うのはなぜですか? 細かいところに興味があり
ます

4

4 に答える 4

9

C++ コンパイラのほとんどの実装では、クラスごとにvtableが生成されます。これは、仮想関数の関数ポインターのテーブルです。他のデータ項目と同様に、vtable の定義は 1 つだけです。一部の C++ コンパイラは、型内で最初に宣言された仮想関数の実装をコンパイルするときに、この vtable を生成します (これにより、vtable の定義が 1 つだけであることが保証されます)。最初の仮想関数の実装を提供できなかった場合、コンパイラは vtable を生成せず、リンカは vtable が見つからないことをリンク エラーで報告します。

ご覧のとおり、これの正確な詳細は、選択したコンパイラとリンカーの実装によって異なります。すべてのツールチェーンが同じというわけではありません。

于 2012-12-25T20:47:19.300 に答える
4

Q: 問題は明らかに、基本クラスに定義されていない非純粋仮想関数があることです。

A: それがあなたの質問に対する答えです :)

Q: 私が宣言して定義していない他の関数と違うのはなぜですか?

A: 単なる「機能」ではないからです。仮想クラス メソッドです。

提案:

  • 3 つの異なるクラスを宣言します。

    1) 簡単な方法

    2)仮想メソッド(上記の「C」など)

    3) 抽象仮想メソッド ( = 0)

  • アセンブリ出力を生成します (GCC の場合は「-S」など)。

  • 3 つのケースを比較します。*コンストラクターが作成されているかどうかに注意してください:)

于 2012-12-25T20:44:35.417 に答える
0

C純粋な仮想基本クラスになりたい場合は、コンパイラに「実装はありません」と伝える必要がありa()ます。これ... a() = 0;は、クラスの宣言を使用して行います。コンパイラは基本クラスのvtableを作成しようとしていますが、存在しません。

于 2012-12-25T21:10:34.047 に答える
0

実際の関数定義の必要性は、いくつかの状況で発生する可能性があります。すでにそのような状況の 1 つを挙げています。関数を呼び出す場合は、関数を定義する必要があります。しかし、これだけではありません。このコードを検討してください

void foo();

int main() {
  void (*p)() = &foo;
}

このコードは決して を呼び出しませんfoo。ただし、コンパイルしようとすると、通常のコンパイラはfoo、リンク段階での定義が欠落していると文句を言います。そのため、関数のアドレスを取得することは (呼び出したことがない場合でも)、たまたま関数を定義する必要がある状況の 1 つです。

ここで、仮想関数の問題の出番です。仮想関数は通常、仮想テーブル (仮想関数の定義へのポインターを含むテーブル) を介して実装されます。このテーブルは、実際に関数を呼び出すかどうかに関係なく、コンパイラによって事前に無条件に作成および初期化されます。コンパイラは基本的に、プログラム内の各非純粋仮想関数のアドレスを暗黙的に取得し、対応するテーブルに配置します。このため、非純粋仮想関数は、呼び出さない場合でも定義が必要です。

正確な仮想メカニズムは実装の詳細であるため、状況によってはさまざまな特定のエラー (関数定義の欠落や仮想テーブル自体の欠落など) が発生する可能性がありますが、これらのエラーの根本的な理由は上で説明したものです。

于 2012-12-25T21:37:03.003 に答える