ケース 1: 関数の複数の定義
module1.cpp:
void f(){}
main.cpp:
void f(){} // error LNK2005: "void __cdecl f(void)" (?func@@YAXXZ) already defined in module1.obj
int main(){}
ケース 2: クラスの複数の定義
module1.cpp:
class C{};
main.cpp:
class C{}; // OK
int main(){}
ケース 1では、予想どおり、(Microsoft) リンカーは同じ関数の 2 つの定義に遭遇し、エラーを発行します。ケース 2では、同じクラスの 2 つの定義が許可されます。
質問 1: 同じクラスの定義が複数ある場合にリンカが文句を言わないのはなぜですか? 関数名はその命令が始まるアドレスの名前であり、クラス名は新しい型の名前であるという事実に関連していますか?
さらに、クラスの異なる定義を使用してもリンカーは文句を言いません (クラス コンストラクターを呼び出す関数を追加して、それらがシンボル テーブルに表示されるようにしました)。
module1.cpp:
class MyClass
{
int n;
public:
MyClass() : n(123){}
};
void func()
{
MyClass c;
}
main.cpp:
class MyClass
{
float n;
public:
MyClass() : n(3.14f){}
};
int main()
{
MyClass c;
}
COD
ファイルに沿ってファイルを生成するようにコンパイラ オプションを設定しOBJ
ます。両方のコンストラクターが同じマングル名 ( ??0MyClass@@QAE@XZ
) の下に表示され、それぞれが独自のユニット (COD
ファイル) にあることがわかります。モジュール内で何らかのシンボルが参照されている場合、リンカは同じモジュールからの定義を使用することが期待されます (存在する場合)。そうでない場合は、それが定義されているモジュールのシンボル定義を使用します。これは、リンカが最初に遭遇したオブジェクト ファイルからシンボルを選択するように見えるため、危険な場合があります。
module1.h:
#ifndef MODULE1_H_
#define MODULE1_H_
void func1();
#endif
module1.cpp:
#include <iostream>
#include "module1.h"
class MyClass
{
int myValue;
public:
MyClass() : myValue(123)
{
std::cout << "MyClass::MyClass() [module1]" << std::endl;
}
void foo()
{
std::cout << "MyClass::foo() [module1]: n = " << myValue << std::endl;
}
};
void func1()
{
MyClass c;
c.foo();
}
module2.cpp:
#include <iostream>
class MyClass
{
public:
MyClass()
{
std::cout << "MyClass::MyClass() [module2]" << std::endl;
}
};
// it is necessary that module contains at least one function that creates MyClass object
void test2()
{
MyClass c;
}
main.cpp:
#include "module1.h"
int main()
{
func1();
}
リンカーに渡すときに、オブジェクト ファイルがこの順序でリストされている場合:
module2.obj module1.obj main.obj
リンカーはMyClass::MyClass
最初の obj ファイルからコンストラクターを選択しますがMyClass::foo
、2 番目のファイルからコンストラクターを選択するため、出力は予期しない (間違った) ものになります。
MyClass::MyClass() [モジュール 2]
MyClass::foo() [モジュール 1]: n = 1
リンカーに渡すときに、オブジェクト ファイルがこの順序でリストされている場合:
module1.obj module2.obj main.obj
リンカーはMyClass
、最初の obj ファイルから両方のメンバーを選択します。
MyClass::MyClass() [モジュール 1]
MyClass::foo() [モジュール 1]: n = 123
質問 2: 上記のエラーにつながる可能性のある複数のクラス定義を許可する方法でリンカーが設計されているのはなぜですか? リンク処理がオブジェクトファイルの順序に依存するのは間違っていませんか?
リンカは、オブジェクト ファイルをスキャンするときに最初に見つけたシンボル定義を選択し、その後のすべての定義の重複を黙って破棄しているようです。
質問 3: これは、リンカがシンボル ルックアップ テーブルを作成する方法ですか?