Xu *_*Hui 5 c++ linker compilation g++ ambiguous
当存在覆盖不明确的函数时,我无法完全理解代码结果。
我有一个库libMy,其中包含两个类A和B.
代码显示如下
// A.h
#ifndef included_A_h
#define included_A_h
class A
{
public:
void print();
};
#endif
Run Code Online (Sandbox Code Playgroud)
// A.cpp
#include "A.h"
#include <iostream>
void A::print()
{
std::cout << "A from library" << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
// B.h
#ifndef included_B_h
#define included_B_h
class A;
class B
{
public:
void printA(A &a);
};
#endif
Run Code Online (Sandbox Code Playgroud)
// B.cpp
#include "B.h"
#include "A.h"
void B::printA(A &a)
{
a.print();
}
Run Code Online (Sandbox Code Playgroud)
我有两个主要功能,它们可以用库生成两个可执行文件。
可以发现 Main*.cpp 看起来很奇怪。为什么需要看起来如此在底部解释。
// MainUsingCPP.cpp
#include <iostream>
#define included_A_h
class A
{
public:
void print()
{
std::cout << "A from Main" << std::endl;
}
};
#include "B.cpp" // note: using B.cpp here
int main()
{
A obj_a;
B obj_b;
obj_b.printA(obj_a);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
// MainUsingH.cpp
#include <iostream>
#define included_A_h
class A
{
public:
void print()
{
std::cout << "A from Main" << std::endl;
}
};
#include "B.h" // note: using B.h here
int main()
{
A obj_a;
B obj_b;
obj_b.printA(obj_a);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
使用下面的行,我们可以编译库,并生成可执行文件。
# generate library
g++ -c A.cpp
g++ -c B.cpp
ar -crv libMy.a A.o B.o
# compile case CPP
g++ MainUsingCPP.cpp -L . -lMy -o MainUsingCPP
# compile case H
g++ MainUsingH.cpp -L . -lMy -o MainUsingH
Run Code Online (Sandbox Code Playgroud)
并运行可执行文件,结果如下
./MainUsingH
A from library
./MainUsingCPP
A from Main
Run Code Online (Sandbox Code Playgroud)
我的问题是:
(1)为什么代码可以编译?
采取MainUsingCPP.cpp和库的考虑,A类被重新定义。所以我们有两个A::print()版本。一个MainUsingCPP.cpp来自图书馆,另一个来自图书馆。在这些阶段,A::print()是模棱两可的。为什么代码可以编译?链接器如何区分它们?链接器如何决定它需要使用哪个版本的函数?
(2)如何理解结果?
为什么导致两个可执行文件不同?为什么链接器A::print()从库中MainUsingH.cpp选择并A::print()从主中选择MainUsingCPP.cpp
为什么 Main.cpp 看起来很奇怪
A是一个类并且B是A的用户。在 中MainUsingCPP.cpp,A的功能似乎可以重新定义。也就是说,A即使 A 没有虚函数,也可以模拟单元测试!
更多可以在fake/mock nonvirtual C++ methods中看到Peter Dotchev的回答
谢谢你的时间!
(1)为什么代码可以编译?
在一个定义规则说
在任何一个翻译单元中只允许定义任何变量、函数、类类型、枚举类型、...或模板
这是满足的,因为每个目标文件对应一个不同的翻译单元。
这么多编译(翻译单元到目标文件) - 现在用于链接:
每个非内联函数或变量的一个且只有一个定义……需要出现在整个程序中(包括任何标准和用户定义的库)。编译器不需要诊断这种违反,但违反它的程序的行为是未定义的。
所以你的程序行为是未定义的,但编译器不需要告诉你,甚至不需要自己确定。
(2)如何理解结果?
不要试图理解未定义的行为,它是未定义的。
如果你想了解你的特定编译器对你的损坏代码做了什么,那么让它为你扩展两个主要的翻译单元(-E用于 GCC)。但是,这实际上是关于您的编译器而不是语言的问题,因为语言没有明确定义这种情况。
| 归档时间: |
|
| 查看次数: |
100 次 |
| 最近记录: |