Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How the code run when exist override ambigous function?

I can not fully understand the code result when exist override ambigous function.

I have a libray libMy, which contains two class A and B.

The code shows as follows

// 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();
}

I have two main function, they can generate two executable file with the library.

It can be found that Main*.cpp looks strange. Why need look like so is explained at the bottom.

// 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;
}

With follow line, we can compile the library, and generate executable file.

# 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

And run the executable file, result shown as follow

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

My problem is that:

(1)Why the code can compile?

Taken MainUsingCPP.cpp and library into consideration, the class A is redefined. So we have two A::print() version. The one from MainUsingCPP.cpp and another from library. At these stage, the A::print() is ambigous. Why the code can compile? How the linker distinguish them? How the linker decide which version of function it needs to use?

(2)How to understand the result?

Why the result in two executable file different? Why the linker choose A::print() from library in MainUsingH.cpp and choose A::print() from Main in MainUsingCPP.cpp

Why Main.cpp looks strange

A is a class and B is A's user. In MainUsingCPP.cpp, the A's funtion seems can be redefined. That is, A is can be mocked for unit test even if A do not have virtual function!

More can see Peter Dotchev's answer in the fake/mock nonvirtual C++ methods

Thanks for your time!

like image 498
Xu Hui Avatar asked Dec 05 '19 09:12

Xu Hui


1 Answers

(1)Why the code can compile?

The One Definition Rule says that

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

and this is satisfied, because each object file corresponds to a different translation unit.

So much for compilation (of translation units to object files) - now for linking:

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.

So your program behaviour is undefined, but the compiler is not required to tell you, or even to determine this itself.


(2)How to understand the result?

Stop trying to understand Undefined Behaviour, it's undefined by definition.

If you want to understand what your specific compiler did with your broken code, then get it to expand the two main translation units for you (using -E for GCC). But, it's really a question about your compiler rather than the language, since the language explicitly didn't define this situation.

like image 144
Useless Avatar answered Oct 12 '22 07:10

Useless