命名空间和静态类成员链接

时间:2013-03-20 19:17:41

标签: c++ gcc linker g++

假设我有两个文件:

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

最后我的问题是:

为什么编译的示例代码没有任何错误,我们可以调用 class foo 私有方法?

使用gcc 4.6.3编译但不仅如此。 我知道编译器不区分这两个符号( func 函数来自命名空间 foo 而私有函数 foo 来自 class foo )。输出 nm

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中的编译器不会链接这两个符号。

2 个答案:

答案 0 :(得分:3)

为什么编译器不抱怨?

请注意,就编译器而言,“class”,“struct”和“namespace”都定义了命名空间。因此编译器相应地修饰符号。如果你在同一个文件中定义类和命名空间,它会抱怨,但是这不是这种情况。

为什么链接器不会抱怨?

您编写代码的方式使func()中定义的namespace foo弱于func()中定义的class foo。基本上,func()中定义的namespace foo只是一个没有实现的签名。您可以看到链接器在运行时解析符号,因为实现不在main.cpp中:

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

这样,由于命名空间和类名恰好相同(导致foo::func的符号相同),链接器在链接时解析符号,找到具有相同符号的强定义,并链接到它

如果您要在func()中实施namespace foo

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

和class.o:

0000000000000000 T __ZN3foo4funcEv
00000000000000a0 S __ZN3foo4funcEv.eh
0000000000000080 s __ZZN3foo4funcEvE12__FUNCTION__
                 U _printf

两者都将同一个函数符号定义为同样强,导致链接器错误。

请记住,链接器不知道命名空间和类之间的区别。它解析了目标代码中的符号。如果发生强烈的重新定义,它只会抱怨。具有一个强定义的一个或多个较弱的定义在链接器世界中完全没有问题。

答案 1 :(得分:2)

因为您已将foo()定义为main.cpp中命名空间的成员,这是编译器对其进行处理的方式。 class / struct / namespace public / private等之间的区别取决于编译器知道函数的定义 - 这里你故意设法愚弄它。

链接器不知道这些区别,它只是解析符号名称,而在编译器的情况下,函数名称的装饰也相同。符号名称的装饰方式在C ++中未指定,因此这是完全有效的行为。