代码 运行 存在时如何覆盖不明确的功能?

How the code run when exist override ambigous function?

我无法完全理解存在重写不明确函数时的代码结果。

我有一个库 libMy,其中包含两个 class AB.

代码如下

// A.h
#ifndef included_A_h
#define included_A_h

class A
{
public:
    void print();
};

#endif
// A.cpp
#include "A.h"
#include <iostream>
void A::print()
{
    std::cout << "A from library" << std::endl;
}
// B.h
#ifndef included_B_h
#define included_B_h
class A;
class B
{
public:
    void printA(A &a);
};

#endif
// B.cpp
#include "B.h"
#include "A.h"
void B::printA(A &a)
{
    a.print();
}

我有两个主要功能,它们可以用库生成两个可执行文件。

可以发现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;
}
// 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;
}

下面一行,我们可以编译库,生成可执行文件。

# 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

和运行可执行文件,结果如下

./MainUsingH
A from library
./MainUsingCPP
A from Main

我的问题是:

(1)为什么代码可以通过?

考虑到MainUsingCPP.cpp和库,classA被重新定义。所以我们有两个 A::print() 版本。一个来自 MainUsingCPP.cpp,另一个来自图书馆。在这些阶段,A::print() 是不明确的。为什么代码可以编译?链接器如何区分它们?链接器如何决定它需要使用哪个版本的函数?

(2)如何理解结果?

为什么两个可执行文件的结果不同? 为什么链接器从 MainUsingH.cpp 中的库中选择 A::print() 并从 MainUsingCPP.cpp

中的 Main 中选择 A::print()

为什么Main.cpp看起来很奇怪

A 是 class 而 BA 的用户。在MainUsingCPP.cpp中,A的功能似乎可以重新定义。也就是说,即使 A 没有虚函数,A 也可以模拟单元测试!

更多可以在fake/mock nonvirtual C++ methods

中查看Peter Dotchev的回答

感谢您的宝贵时间!

(1)Why the code can compile?

One Definition Rule 表示

Only one definition of any variable, function, class type, enumeration type, ... or template is allowed in any one translation unit

这就满足了,因为每个目标文件对应不同的翻译单元。

编译(将翻译单元转换为目标文件)到此为止 - 现在进行链接:

One and only one definition of every non-inline function or variable ... is required to appear in the entire program (including any standard and user-defined libraries). The compiler is not required to diagnose this violation, but the behavior of the program that violates it is undefined.

所以你的程序行为是未定义的,但是编译器不需要告诉你,甚至不需要自己确定。


(2)How to understand the result?

停止试图理解未定义的行为,它的定义是未定义的。

如果您想了解您的特定编译器对您损坏的代码做了什么,然后让它为您扩展两个主要翻译单元(对 GCC 使用 -E)。但是,这实际上是关于您的编译器而不是语言的问题,因为语言明确没有定义这种情况。