案例一:一个函数的多个定义
模块1.cpp:
void f(){}
主.cpp:
void f(){} // error LNK2005: "void __cdecl f(void)" (?func@@YAXXZ) already defined in module1.obj
int main(){}
案例2:一个类的多个定义
模块1.cpp:
class C{};
主.cpp:
class C{}; // OK
int main(){}
在案例 1中,正如预期的那样,(Microsoft)链接器遇到相同函数的两个定义并发出错误。在案例 2中,它允许同一类的两个定义。
问题 1:为什么在同一个类有多个定义时链接器不报错?这是否与函数名只是其指令开始的地址的名称而类名是新类型的名称这一事实有关?
此外,即使我们使用不同的类定义,链接器也不会抱怨(我添加了调用类构造函数的函数,因此它们出现在符号表中):
模块1.cpp:
class MyClass
{
int n;
public:
MyClass() : n(123){}
};
void func()
{
MyClass c;
}
主.cpp:
class MyClass
{
float n;
public:
MyClass() : n(3.14f){}
};
int main()
{
MyClass c;
}
我设置了编译器选项,以便它COD
沿着文件生成OBJ
文件。我可以看到两个构造函数都出现在相同的名称(??0MyClass@@QAE@XZ
)下,每个都在自己的单元(COD
文件)中。如果在模块中引用了某个符号,则链接器将使用同一模块中的定义(如果存在)。如果不是,它将使用定义它的模块中的符号定义。这可能很危险,因为链接器似乎从它遇到的第一个目标文件中选择符号:
模块1.h:
#ifndef MODULE1_H_
#define MODULE1_H_
void func1();
#endif
模块1.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();
}
模块2.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;
}
主.cpp:
#include "module1.h"
int main()
{
func1();
}
如果目标文件在传递给链接器时按此顺序列出:
module2.obj module1.obj main.obj
MyClass::MyClass
链接器将从第一个 obj 文件中选择构造函数,但从MyClass::foo
第二个文件中选择构造函数,因此输出是意外的(错误的):
MyClass::MyClass() [module2]
MyClass::foo() [module1]: n = 1
如果目标文件在传递给链接器时按此顺序列出:
module1.obj module2.obj main.obj
MyClass
链接器将从第一个 obj 文件中选择两个成员:
MyClass::MyClass() [module1]
MyClass::foo() [module1]: n = 123
问题 2:为什么链接器的设计方式允许多个类定义,这会导致上述错误?链接过程取决于目标文件的顺序,这不是错的吗?
似乎链接器在扫描目标文件时选择了它遇到的第一个符号定义,然后默默地丢弃所有后续定义重复。
问题 3:链接器是这样构建其符号查找表的吗?