对头文件外部定义的成员函数的 C++ 未定义引用

c++ undefined reference to member function defined outside of header file

提问人:hewy 提问时间:8/9/2014 更新时间:8/9/2014 访问量:4859

问:

在我的印象中,您可以在一个文件中定义一个类的成员函数,然后在另一个文件中使用这些函数,只要两个文件都经过编译并发送到链接器即可。但是,如果我使用 g++ (4.6.4),这样做会给我一个未定义的引用错误。有趣的是,使用英特尔编译器 (icpc 11.0) 不会出现错误,一切正常。我是否可以在 g++ 中设置一些标志来使其工作,或者英特尔编译器是否让我逃脱了我不应该做的事情?以下是重现我问题的一些代码:

class.h:

#ifndef _H
#define _H

typedef class
{
    public:
            int a;
            int b;

            void set(int x, int y);
            int add(void);
} Test;

#endif

类.cpp:

#include "class.h"

void Test::set(int x, int y)
{
    a = x;
    b = y;
}

int Test::add(void)
{
    return a+b;
}

main.cpp:

#include <cstdio>
#include "class.h"

int main(void)
{
    Test n;
    n.set(3, 4);
    printf("%d\n", n.add());

    return 0;
}

为了编译,我做:

$ g++ class.cpp main.cpp -o test 
/tmp/ccRxOI40.o: In function `main':
main.cpp:(.text+0x1a): undefined reference to `Test::set(int, int)'
main.cpp:(.text+0x26): undefined reference to `Test::add()'
collect2: ld returned 1 exit status
C G++ 未定义引用 成员函数

评论

5赞 Captain Obvlious 8/9/2014
不需要,就做。typedefclass Test {};
2赞 Baum mit Augen 8/9/2014
_H保留用于实现,选择合法标识符。
0赞 hewy 8/9/2014
@dyp g++ 的额外标志没有区别(但我不得不更改为 )。我认为我所做的很好,但以防万一我尝试过,最后得到了同样的错误-std=c++11-std=c++0xg++ class.cpp main.cppg++ -c class.cpp -o class.og++ -c main.cpp -o main.og++ class.o main.o -o test
0赞 dyp 8/9/2014
哎呀,我看错了。对不起。额外的标志告诉编译器生成更多警告,这可能会揭示一些错误。为了更便携,默认是 IIRC,对 C++98 的一些扩展。-Wall -Wextra -pedantic-std=...gnu++98

答:

5赞 jxh 8/9/2014 #1

好吧,这很奇怪,但发生的事情是这个结构:

typedef class
{
    public:
            int a;
            int b;

            void set(int x, int y);
            int add(void);
} Test;

虽然编译器在语义上没有将 legal 视为:

class Test
{
    public:
            int a;
            int b;

            void set(int x, int y);
            int add(void);
};

该版本将方法用于文件,如输出中所示:typedefstaticnm

$ nm class.o
0000000000000024 t _ZN4Test3addEv
0000000000000000 t _ZN4Test3setEii
                 U __gxx_personality_v0

虽然版本使它们成为正确的方法:class Test

$ nm class2.o
0000000000000024 T _ZN4Test3addEv
0000000000000000 T _ZN4Test3setEii
                 U __gxx_personality_v0

这就是链接器找不到符号的原因。

编辑:至于为什么会发生这种情况,似乎是由于解释标准如何指定将名称视为类名的问题。较新的编译器似乎没有表现出同样的问题。此问题中报告的问题在 g++ 4.4.7 中重现。typedef

如果您将文件中的代码移动到并且仅编译,则事情将起作用。或者,可以将方法定义内联到 中。class.cppmain.cppmain.cppclass.h

如果要将它们保留为单独的翻译单元,则需要更改文件,以便使用 way 而不是使用 on 匿名类来定义类。class.hclass Testtypedef

评论

0赞 hewy 8/9/2014
谢谢,这奏效了,你知道为什么会这样吗?我以为这两个版本是等价的。
0赞 jxh 8/9/2014
因为你声明了一个匿名类。由于该类实际上没有名称,因此 C++ 编译器认为尝试解析与该类的方法相关的全局符号是违规的。因此,它使您的方法文件成为本地文件。这是一个相关的问题。.
1赞 Ben Voigt 8/9/2014
引入的名称是类名*,而标准要求在类外部定义的每个成员函数都使用“其类名”(单数)。不是“其类名之一”(复数),其中包括由 创建的类名。似乎该标准可以对此进行一些澄清。typedeftypedef
0赞 jxh 8/9/2014
@BenVoigt:谢谢,所以我应该把“不洁食”的措辞改成表明正确的处理方式是模棱两可的?
1赞 aschepler 8/9/2014
但是 7.1.3/9 呢?“如果 typedef 声明定义了一个未命名的类(或枚举),则声明声明为该类类型(或枚举类型)的第一个 typedef-name 用于表示类类型(或枚举类型),仅用于链接目的 (3.5)。”3.5 特别指出,此类类及其成员具有外部联系。