15

私はこのクラスを持っています、

class Base {
    public:
        void foo();
};

int main()
{
    Base b;
}

mainfoo()定義はありませんが、エラーなしでコンパイルされます。しかしb.foo();、コンパイルエラーになります。

さらに、constructorandについてもoperator=、定義せずに宣言するだけで、トリガーしない限りコンパイルされます。

質問

Base繰り返しますが、仮想関数をに追加します。

class Base {
    public:
        void foo();
        virtual void bar();  // no defition is gonna be provided.
};

今、mainコンパイルできません。代わりにエラーが発生します:

Base の vtable への未定義の参照

main以前は呼び出されない限りコンパイルできたので、これは私を少し混乱させますfoo()が、今は追加bar()して、まったく呼び出されません。

この場合、なぜコンパイルされないのですか?

4

4 に答える 4

16

これは厳密にはコンパイラ次第です。どちらの場合も、診断は必要ありません。

10.3 仮想関数 [class.virtual]

9virtualクラスで宣言された関数は、そのクラスで定義されるか、純粋 (10.4) で宣言されるか、またはその両方でなければなりません。ただし、診断は必要ありません (3.2)。[鉱山を強調]

なぜこれが起こるのかを理解するために、それがどのように機能するかを見てみましょう.

各翻訳単位はオブジェクト ファイルを生成し、各オブジェクト ファイルにはエクスポート (エクスポートされたシンボル) とインポート (必要なシンボル) が含まれます。

最初の例は単純です。インポートfooは、使用時にのみ必要です。リンカがシンボルを探す理由はないので、そうしません。

メソッドを使用する 2 つ目のvirtual方法は、もう少し複雑です。ほとんどのコンパイラ (すべてではないにしても) は、有効な仮想関数テーブルを必要としていました。つまり、リンク時に、非純粋仮想メソッドを宣言したすべてのクラスは、それらのメソッドをエクスポートする必要があります。virtualこれは、関数が呼び出されたかどうかを実装が実際に認識していないため (多態的に呼び出される可能性があります) 、non-case よりも厳密です。

于 2013-01-14T10:21:57.913 に答える
5

リンカーは object を検索する必要がないため、最初のバージョンはコンパイルできますfoo。どこにも使われていません。

ただし、仮想関数を作成する場合、vtableの構築(動的ディスパッチ用) には関数のアドレスが必要でありBase::bar(参照を作成するため)、リンカーはその実装を見つける必要があります。

于 2013-01-14T10:19:22.353 に答える
3

ポリモーフィック オブジェクト (少なくとも 1 つの仮想関数を持つクラスのインスタンス) が作成される場合、仮想テーブルを指す仮想テーブル ポインターが必要です。各ポリモーフィック クラスには、一度構築される仮想テーブルがあります。仮想テーブルが不完全であることを許可するかどうかは、コンパイラの実装次第です。このテーブルは、仮想機能の定義に基づいて入力されます。

仮想関数が実装されておらず、その関数が純粋な仮想関数ではない場合、仮想テーブルが不完全であるため、一部のコンパイラはコンパイラ エラーを返します。

単純な (非仮想) 関数の場合、関数宣言は使用されなければ無視されます。使用しても、コンパイラ エラーではなくリンカ エラーが発生します。

于 2013-01-14T10:25:00.293 に答える
1

クラスで宣言された仮想関数は、定義されているか、そのクラスで純粋に宣言されているか、またはその両方でなければなりません (C++03 標準)。

于 2013-01-14T10:24:04.637 に答える