18

first.cppクラス定義を含むこのファイルを検討し、次を使用します。

#include <iostream>

struct Foo
{
    Foo(){ std::cout << "Foo()" << std::endl; }
    ~Foo(){ std::cout << "~Foo()" << std::endl; }
};

int main(){
    Foo f;
    return 0;
}

もう1つはsecond.cpp、競合するクラス定義を含みます。

#include <iostream>

struct Foo
{
    Foo();
    ~Foo();
};

Foo::~Foo(){ std::cout << "wrong ~Foo()" << std::endl; }

同じ名前の関数が2つ定義されている場合、リンカはシンボルの重複について文句を言いますが、クラスメソッドが重複しているこれらのファイルはエラーなしでコンパイルされます。

私はこれらのコマンドでコンパイルしました:

$ g++ -c second.cpp -o second
$ g++ second first.cpp -o first

2番目の呼び出しの引数を並べg++替えても、出力は変更されません。

そして、firstが実行されると、これが出力になります。

$ ./first
Foo()
wrong ~Foo()

リンカが重複クラスメソッドを許可するのはなぜですか?明らかに許可されているのなら、なぜwrong ~Foo()印刷されるのですか?

4

1 に答える 1

16

繰り返しますが、未定義の動作です。あなたのプログラムには、 のデストラクタの定義が複数ありますFoo。これは、ODR に違反していることを意味します。プログラムが間違っていて、何かが起こる可能性があります。

リンカーがそれを取得しないのはなぜですか? 関数がクラス定義内で定義されている場合、それは暗黙的にinlineです。コンパイラは通常、これらの関数を「弱いシンボル」としてマークします。次に、リンカーはすべての翻訳単位を取得し、シンボルの解決を試みます。弱いシンボルは、必要に応じてリンカーによって削除されます (つまり、シンボルが既に別の場所で定義されている場合)。

プログラムの実際の出力の時点で、コンパイラーは実際にはコンストラクターへの呼び出しをインライン化しなかったため、実行時にリンカーによって残されたシンボル (非弱いシンボル) にディスパッチされたようです。


リンカがメソッドの重複を許可するのはなぜですか?

すべて (ただし、多くても 1 つ) が弱いシンボルであるため (つまりinline)

この場合、間違った ~Foo() が出力されるのはなぜですか?

呼び出しがインライン化されておらず、リンカーが弱いシンボルを削除したため

于 2012-05-25T13:51:00.553 に答える