【问题标题】:Why defining classes in header files works but not functions为什么在头文件中定义类有效但不是函数
【发布时间】:2015-04-08 22:40:54
【问题描述】:

我有这段小代码:

文件 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;
}

如果我理解得很好,我们不能在头文件中定义函数并在不同的单元翻译中使用它(除非函数被声明为静态)。宏 MODA 不会在每个单元翻译中定义,因此主体保护不会阻止标题被复制以代替每个 #include "modA.h"。这会导致函数在不同的地方定义,链接器会抱怨它。对吗?

但是为什么可以对类以及类的方法这样做。为什么链接器不抱怨它? 不就是重新定义一个类吗?

谢谢

【问题讨论】:

    标签: c++ header-files


    【解决方案1】:

    在类定义体中定义成员函数时,默认为inline。如果您在 .h 文件中限定非成员函数 inline,它将正常工作。

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

    3.2 一个定义规则

    3 每个程序都应包含该程序中 odr 使用的每个非内联函数或变量的确切定义; ...

    如果您在 .h 文件中定义类定义主体之外的成员函数并且没有显式添加 inline 限定符,则会遇到相同的错误。

    【讨论】:

    • Re“如果没有 inline 限定符,.h 文件中定义的非成员函数就像外部函数一样”,inline 函数默认为 extern
    【解决方案2】:

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

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

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

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

    【讨论】:

      【解决方案3】:

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

      这是不正确的。您可以,并且在提供的代码中CAdd::getResult 就是这样一种功能。

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

      实际上,inline 说明符告诉链接器任意选择一个定义(如果有多个)。

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

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

      标准中处理此问题的部分称为One Definition Rule,通常缩写为ODR

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-07-25
        • 1970-01-01
        • 1970-01-01
        • 2013-01-17
        • 1970-01-01
        • 1970-01-01
        • 2010-10-28
        • 2020-05-22
        相关资源
        最近更新 更多