I have an interesting problem that crept up and I was wondering why GCC/G++ doesn't catch this and throw some kind of error.
Apologies for how many files this takes, but I've reduced the problem as much as possible.
Interface.H
class BaseClass {
public:
virtual void hello() = 0;
};
void rememberClass(BaseClass* foo);
void callFunc();
Interface.C
#include "Interface.H"
namespace {
typedef void (BaseClass::*memfunptr)();
memfunptr func;
BaseClass* obj;
}
void rememberClass(BaseClass* foo) {
func = &BaseClass::hello;
obj = foo;
}
void callFunc() {
(obj->*func)();
}
Class1.H
class Class1 {
public:
Class1();
};
Class2.H
class Class2 {
public:
Class2();
};
Class1.C
#include "Interface.H"
#include "Class1.H"
#include <iostream>
class HelloClass : public BaseClass {
public:
HelloClass() {}
void hello() {
std::cout << "Calling Hello" << std::endl;
}
};
Class1::Class1() {
HelloClass* foo = new HelloClass();
rememberClass(foo);
}
Class2.C
#include "Interface.H"
#include "Class2.H"
#include <iostream>
class HelloClass : public BaseClass {
public:
HelloClass() {}
void hello() {
std::cout << "Calling Hello 2" << std::endl;
}
};
Class2::Class2() {
HelloClass* foo = new HelloClass();
rememberClass(foo);
}
main.C
#include "Class2.H"
#include "Interface.H"
int main(int argc, char** argv) {
Class2 a;
callFunc();
}
Output
g++ Class1.C Interface.C main.C Class2.C
./a.out
Calling Hello
As you can see above, Even though I am constructing a Class2
, it prints the output from Class1
. This is because the vtable for HelloClass
in both the Class1
and Class2
have the same address for HelloClass::hello(), and it is the address of the function in Class1.C
I'm assuming that this is because when GCC is doing the linking, it sees vtables for classes with the same mangled names and just discards one of them. But should it warn about this? Or even cause an error. I have tried with -Wall and -Wextra but nothing is mentioned.
No, the standard explicitly allows the compiler to silently do the wrong thing in this case.
Explicitly, you are causing Undefined Behavior by having two conflicting definitions for the same decorated name in the same program.
It is called the ODR "One Definiton Rule".
Chapter 3.2.