为什么在头文件中定义类有效但不是函数

时间:2015-04-08 22:40:54

标签: c++ header-files

我有一小段代码:

文件modA.h
#ifndef MODA
#define MODA

class CAdd {
public:
    CAdd(int a, int b) : result_(a + b) { }

    int getResult() const { return result_; }

private:
    int result_;
};

/*
int add(int a, int b) {
    return a + b;
}
*/
#end
文件calc.cpp
#include "modA.h"

void doSomeCalc() {
    //int r = add(1, 2);
    int r = CAdd(1, 2).getResult();
}
文件main.cpp
#include "modA.h"

int main() {
    //int r = add(1, 2);
    int r = CAdd(1, 2).getResult();
    return 0;
}

如果我理解的话,我们不能在头文件中定义一个函数并在不同的单元转换中使用它(除非该函数被声明为static)。宏MODA不会在每个单位翻译中定义,因此身体防护不会阻止标题被复制来代替每个#include“modA.h”。这会导致函数在不同的地方定义,并且链接器会抱怨它。这是对的吗?

但是为什么有可能用类和类的方法来实现。链接器为什么不抱怨它? 这不是一个阶级的重新定义吗?

谢谢

3 个答案:

答案 0 :(得分:5)

当在类定义的主体中定义成员函数时,默认情况下它们是inline。如果您在.h文件中限定非成员函数inline,它将正常工作。

如果没有inline限定符,.h文件中定义的非成员函数将编译在包含.h文件的每个.cpp文件中。这违反了标准中的以下规则:

  

3.2一个定义规则

     

3每个程序应包含该程序中每个非内联函数或变量的一个定义; ...

如果在.h文件中定义类定义主体之外的成员函数并且未明确添加inline限定符,则会出现相同的错误。

答案 1 :(得分:4)

  

如果我理解的话,我们不能在头文件中定义一个函数并在不同的单位翻译中使用它(除非该函数被声明为静态)

这是不正确的。您可以,并且在显示的代码中CAdd::getResult就是这样一个函数。

为了支持多个翻译单元中标题的一般使用,它提供了函数的多个竞争定义,它必须是inline。类定义中定义的函数(如getResult)自动为inline。在类定义之外定义的函数需要显式声明inline

实际上,inline说明符告诉链接器只是随意选择其中一个定义,如果有几个。

遗憾的是,没有简单的语法可以对数据执行相同的操作。也就是说,数据不能仅被声明为inline。但是,类模板的静态数据成员可以免除,并且具有extern链接的inline函数可以包含static局部变量,因此编译器也需要有效地支持相同的数据机制

inline函数默认具有extern链接。由于inline也可以作为优化提示,因此可以使用inline static函数。对于默认extern链接的情况,请注意标准然后要求在每个使用它的翻译单元中相同地定义函数。

处理此问题的标准部分称为一个定义规则,通常缩写为 ODR

在C ++ 11中,ODR是§3.2“一个定义规则”。具体而言,C ++11§3.2/ 3规定了每个相关翻译单元中inline函数定义的要求。然而,在C +11§7.1.2/ 4中重复了关于“函数说明符”的要求。

答案 2 :(得分:4)

多个翻译单元可能需要在编译时定义类,因为除非类的定义可用,否则无法知道类的成员类型(甚至是否存在)。 (因此,必须允许您在多个翻译单元中定义一个类。)另一方面,翻译单元只需要声明一个函数,因为只要它知道如何调用该函数,编译器就可以离开作业将函数的实际地址插入链接器。

但这是有代价的:如果一个类在一个程序中多次定义,所有的定义必须相同,如果它们不相同,那么你可能会得到奇怪的链接器错误,或者如果程序链接,它可能会发生段错误。

对于功能,您没有此问题。如果多次定义函数,链接器会通知您。这很好,因为它避免了在给定程序中意外定义具有相同名称和签名的多个函数。如果要覆盖它,可以声明函数inline。然后应用与类相同的规则:必须在每个翻译单元中定义函数,在该翻译单元中以某种方式使用它( odr-used ,确切地说),并且所有定义必须是相同。

如果在类定义中定义了一个函数,则会有一个特殊的规则,即它是隐式内联的。如果不是这种情况,那么只要在类定义中定义了至少一个函数,就不可能有一个类的多个定义,除非你遇到标记所有这些函数inline的麻烦。