命名空间和静态类成员联

问题描述:

可以说我有两个文件:命名空间和静态类成员联

/** 
* class.cpp 
*/ 
#include <stdio.h> 
class foo 
{ 
private: 
     int func(); 
}; 

int foo::func(void) 
{ 
     printf("[%s:%d]: %s\n", __FILE__, __LINE__, __FUNCTION__); 
     return -1; 
} 

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

编译如下:

g++ -o a.out main.cpp class.cpp 

有一个从可执行文件的输出:

[class.cpp:15]: func 
[main.cpp:14]: ret=-1 

最后我的问题:

为什么这个示例代码编译没有任何错误,我们都能够调用私有方法类Foo

编译与gcc 4.6.3但不仅。 我知道,编译器并不区分这两个符号(从命名空间和私有函数类FooFUNC功能)。从纳米输出:

nm class.o 
00000000 T _ZN3foo4funcEv 
00000017 r _ZZN3foo4funcEvE12__FUNCTION__ 
     U printf 

nm main.o 
     U _ZN3foo4funcEv 
00000000 T main 
     U printf 

我想问一下这种行为是否正确与否?恕我直言,这是不正确的行为,它是不安全的(打破封装)。

我想提一下,visual studio 2008的编译器不会链接这两个符号。

因为您已将foo()定义为main.cpp中的命名空间的成员,所以它是编译器如何处理它的。 class/struct/namespace public/private等之间的区别取决于编译器知道函数的定义 - 在这里你故意设置它来欺骗它。

链接器不知道这种区别,它只是解析符号名称,而在编译器中,函数名称的装饰风格是相同的。符号名称的装饰方式在C++中是未指定的,所以这是完全有效的行为。

为什么编译器不会抱怨?

请注意,“类”,“结构”和“命名空间”都只是在编译器方面定义了一个名称空间。所以编译器会相应地装饰符号。如果您在同一个文件中定义了类和名称空间,它会发生抱怨,但在这里并不是这样。

为什么连接器没有抱怨?

你所编写的代码的方式,叶比class foo定义的func()namespace foo弱定义的func()。基本上,namespace foo中定义的func()只是一个没有实现的签名。你可以看到,这是留给链接程序在运行时解析符号,因为执行不main.cpp

nm main.o 
     U _ZN3foo4funcEv 
//Here^^^^ 

这种方式,因为命名空间和类名碰巧相同(导致相同的符号对于foo::func),链接器在链接时解决该符号,找到具有相同符号的强定义,并与其链接。

如果要实现在namespace foofunc()还有:

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

你会看到链接器抱怨有:

duplicate symbol foo::func()  in: 
/var/folders/.../class.o 
/var/folders/.../main.o 
ld: 1 duplicate symbol for architecture x86_64 

如果你看看main.o这个你会看到:

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 

两者都定义相同的函数符号同样强,导致链接器错误。

请记住,链接器不知道名称空间和类之间的区别。它解析了目标代码中的符号。只有强烈的重新定义发生时,它才会抱怨。在一个强大的定义中,一个或多个较弱的定义在链接器世界中完美无瑕。

+0

好的,我明白一切,但问题是为什么有这样的gcc编译器的行为。 Windows编译器能够区分这些符号。命名空间和类是不一样的,我愿意吗?编译器应该知道命名空间和类之间的所有区别,不是吗? – pako 2013-03-20 20:11:30

+0

编译器知道,但链接器不知道。但是,编译器会将每个文件转换为单独的对象文件。您定义这两个文件的方式是它们与编译器完全分离。 VS做的很好,但不需要做。因此,不是由gcc完成的。铿锵也编译这没有抱怨的方式。 – meyumer 2013-03-20 20:15:54

+0

我知道编译器知道,但我的意思是编译器为这些函数生成完全相同的符号,即使它们完全不同。所以也许gcc应该和VS编译器一样?它可以避免一些潜在的错误。 – pako 2013-03-20 20:23:34