Reputation: 141
Lets say I have two files:
/**
* class.cpp
*/
#include <stdio.h>
class foo
{
private:
int func();
};
int foo::func(void)
{
printf("[%s:%d]: %s\n", __FILE__, __LINE__, __FUNCTION__);
return -1;
}
and
/**
* main.cpp
*/
#include <stdio.h>
namespace foo
{
int func(void);
}
int main(void)
{
int ret = foo::func();
printf("[%s:%d]: ret=%d\n", __FILE__, __LINE__, ret);
return 0;
}
compiled as follows:
g++ -o a.out main.cpp class.cpp
There is an output from executable file:
[class.cpp:15]: func
[main.cpp:14]: ret=-1
And finally my question:
Why is this sample code compiled without any errors and we are able to invoke private method of class foo ?
Compiled with gcc 4.6.3 but not only. I know that the compiler does not distinguish those two symbols (func function from namespace foo and private function foo from class foo). Output from nm:
nm class.o
00000000 T _ZN3foo4funcEv
00000017 r _ZZN3foo4funcEvE12__FUNCTION__
U printf
nm main.o
U _ZN3foo4funcEv
00000000 T main
U printf
I would like to ask whether this behavior is correct or not? IMHO this is not correct behavior and it is not safe at all (breaks down encapsulation).
I would like to mention that compiler from visual studio 2008 does not link those two symbols.
Upvotes: 11
Views: 481
Reputation: 5064
Why does the compiler not complain?
Note that "class", "struct" and "namespace" all define a namespace as far as the compiler is concerned. So the compiler decorates the symbols accordingly. It would complain if you define both the class and the namespace in the same file, however it is not the case here.
Why does the linker not complain?
The way you have written the code, leaves the func()
defined in namespace foo
weaker than the func()
defined in class foo
. Basically, the func()
defined in namespace foo
is just a signature without implementation. You can see that it is left to the linker to resolve the symbol at runtime because the implementation is not in main.cpp
:
nm main.o
U _ZN3foo4funcEv
//Here^^^^
This way, since the namespace and class names happened to be the same (resulting in same symbols for foo::func
), linker solves the symbol at linktime, finds a strong definition with the same symbol, and links against it.
If you were to implement the func()
in the namespace foo
as well:
/**
* main.cpp
*/
#include <stdio.h>
namespace foo
{
int func(void) {
printf("NM_FOO [%s:%d]: %s\n", __FILE__, __LINE__, __FUNCTION__);
return -1;
};
}
int main(void)
{
int ret = foo::func();
printf("[%s:%d]: ret=%d\n", __FILE__, __LINE__, ret);
return 0;
}
You would have seen that the linker complains with:
duplicate symbol foo::func() in:
/var/folders/.../class.o
/var/folders/.../main.o
ld: 1 duplicate symbol for architecture x86_64
If you look at the main.o this time you would see:
0000000000000064 T __ZN3foo4funcEv
0000000000000158 S __ZN3foo4funcEv.eh
00000000000000e0 s __ZZN3foo4funcEvE12__FUNCTION__
0000000000000000 T _main
0000000000000128 S _main.eh
U _printf
And class.o:
0000000000000000 T __ZN3foo4funcEv
00000000000000a0 S __ZN3foo4funcEv.eh
0000000000000080 s __ZZN3foo4funcEvE12__FUNCTION__
U _printf
both of which defines the same function symbol equally strong, resulting in the linker error.
Remember, the linker does not know about the difference between a namespace and a class. It resolves the symbols that are in the object code. It will only complain if strong re-definitions happen. One or multiple weaker definitions with one strong definition is perfectly fine in the linker world.
Upvotes: 3
Reputation: 37192
Because you've defined foo()
as being a member of a namespace in main.cpp that's how the compiler has treated it. The distinction between class/struct/namespace public/private etc depends on the compiler knowing the definition of the function - here you've deliberately set out to fool it.
The linker doesn't know about such distinctions, it simply resolves symbol names, and in the case of your compiler the decoration of the function names winds up the same. The way symbol names are decorated is unspecified in C++ so this is perfectly valid behaviour.
Upvotes: 2