考虑这个包含类定义的文件first.cpp
并使用:
#include <iostream>
struct Foo
{
Foo(){ std::cout << "Foo()" << std::endl; }
~Foo(){ std::cout << "~Foo()" << std::endl; }
};
int main(){
Foo f;
return 0;
}
和另一个second.cpp
,包含冲突的类定义:
#include <iostream>
struct Foo
{
Foo();
~Foo();
};
Foo::~Foo(){ std::cout << "wrong ~Foo()" << std::endl; }
当有两个定义了相同名称的函数时,链接器会抱怨重复的符号,但这些具有重复类方法的文件在没有错误的情况下编译。
我用这些命令编译:
$ g++ -c second.cpp -o second
$ g++ second first.cpp -o first
将参数重新排序到第二个g++
调用不会改变输出。
当first
运行时,这是输出:
$ ./first
Foo()
wrong ~Foo()
为什么链接器允许重复的类方法?如果显然允许,为什么要打印wrong ~Foo()
?
答案 0 :(得分:15)
再次,未定义的行为。您的程序对Foo
的析构函数有多个定义,这意味着它违反了ODR。该计划是错误的,任何事情都可能发生。
链接器为什么不拿起它?在类定义中定义函数时,它隐式为inline
。编译器通常将这些函数标记为“弱符号”。然后链接器获取所有转换单元并尝试解析符号。如果需要,链接器将丢弃弱符号(即,如果符号已在其他位置定义)。
从程序的实际输出开始,看起来编译器实际上没有内联对构造函数的调用,因此在运行时调度到链接器留下的符号(非弱的)
为什么链接器允许有重复的方法?
因为所有(但最多只有一个)是弱符号(即inline
)
为什么,在这种情况下,错误的〜Foo()会打印出来?
因为调用没有内联,链接器删除了弱符号