【问题标题】:Basic ODR violation: member functions in .h files基本 ODR 违规:.h 文件中的成员函数
【发布时间】:2021-03-08 11:08:09
【问题描述】:

免责声明:这可能是一个基本问题,但我是一名理论物理学家,通过训练努力学习正确编码,所以请多多包涵。

假设我想为一个相当复杂的物理系统建模。在我的理解中,对这个系统进行建模的一种方法是将它作为一个类来引入。但是,由于涉及到系统,类会很大,可能包含许多数据成员、成员函数和子类。将主程序和此类放在一个文件中会非常混乱,因此为了更好地了解项目,我倾向于将类放在单独的 .h 文件中。这样我就会有类似的东西:

//main.cpp

#include "tmp.h"

int main()
{
    myclass aclass;

    aclass.myfunction();

    return 0;
}

// tmp.h

class myclass
{
    // data members
    double foo;
    double bar;

    public: 

    // function members
    double myfunction();
};

double myclass::myfunction()
{
    return foo + bar;
}

然而,这相当于我的新编译器中的以下编译器警告:function definitions in header files can lead to ODR violations。那么我的问题是:处理这种情况的首选方法是什么?我想我可以将 tmp.h 变成 tmp.cpp,但据我所知,这是 .h 文件的预期用途?

【问题讨论】:

  • 要么把inline放在double myclass::myfunction()前面,要么把定义放在tmp.cpp中
  • 令我惊讶的是,似乎没有明显的重复,尽管关于非成员函数也有类似的问题。但是,成员函数的规则略有不同,因此其他 Q 不是重复的。

标签: c++ one-definition-rule hfile


【解决方案1】:

通常,类定义放在“.h”文件中,其成员函数的定义放在“.cpp”文件中。

如果你想在头文件中定义成员函数,你需要声明它们inline,或者将它们写在类定义中(这使得它们隐式内联)。

【讨论】:

    【解决方案2】:

    添加到其他答案:

    这是一个函数定义

    double myfunction()
    {
        return foo + bar;
    }
    

    这是一个函数声明

    double myfunction();
    

    声明的目的是向其他代码声明函数的唯一签名。函数可以多次声明,但只能有一个定义,因此有ODR(One Definition Rule)。

    作为开始的基本规则,将函数声明放在头文件中,并将定义放在源文件中。

    不幸的是,在 C++ 中,事情很快变得更加复杂。

    只有函数签名可用的问题是你不能轻易地优化函数中的代码,因为你看不到它。为了解决这个问题,C++ 允许在几种情况下将函数定义放在头文件中。

    【讨论】:

    • 哦,我明白了!我喜欢声明和定义之间的分离,感谢您充实它!
    【解决方案3】:

    您必须使用inline 关键字或将myclass 的定义放在.cpp 文件中。

    myclass.hpp

    #ifndef MY_CLASS_H
    #define MY_CLASS_H
    
    class myclass
    {
    public:
        double myfunction( );
    private: 
        double foo;
        double bar;
    };
    
    #endif
    

    myclass.cpp

    #include "myclass.hpp"
    
    double myclass::myFunction( ) 
    {
        return foo + bar;
    }
    

    或者您可以使用 inline 在标题 (myclass.hpp) 中定义函数。

    #ifndef MY_CLASS_H
    #define MY_CLASS_H
    
    class myclass
    {
    public:
        double myfunction( );
    private: 
        double foo;
        double bar;
    };
    
    inline double myclass::myFunction( ) 
    {
        return bar + foo;
    }
    #endif
    

    如果您在class 声明中定义myFunction 函数,则可以省略inline 关键字的使用。

    #ifndef MY_CLASS_H
    #define MY_CLASS_H
    
    class myclass
    {
    public:
        double myfunction( )
        {
            return foo + bar;
        }
    private: 
        double foo;
        double bar;
    };
    
    #endif
    

    【讨论】:

      【解决方案4】:

      ODR 代表One Definition Rule。这意味着一切都应该有一个且只有一个定义。现在,如果您在头文件中定义一个函数,则包含该头文件的每个翻译单元都将获得一个定义。这显然违反了 ODR。这就是编译器警告的内容。你有几种方法可以解决这个问题:

      1. 将函数声明移至cpp 文件。这样就有了一个单一的定义。
      2. 创建函数inline。这意味着该函数可能有多个定义,但您确定所有定义都是相同的,链接器可能会使用其中一个而忽略其余的。
      3. 创建函数static(不适用于类方法)。当函数为static 时,每个翻译单元都会获得它自己的方法副本,但它们都是不同的函数,属于一个且仅属于一个编译单元。所以没关系。

      【讨论】:

      • "现在,如果您在头文件中定义一个函数,则包含该头文件的每个翻译单元都将获得一个定义。"是的,这太糟糕了,我不知道,但是好吧,inline 是你的朋友,或者只是将函数保存在 .cpp 文件中!感谢您清除 ODR 的含义!
      • @storluffarn 如果您知道#include 的作用,那么违反 ODR 就不足为奇了。我也不建议你到处撒inline。一般来说,你 inline 非常短的方法并将其他所有内容移动到 cpp 文件中。
      • 当然,根据这里的其他建议,我将开始将类定义和声明分离到单独的文件中,这应该这样做。 IE。有一个带有类和函数声明的 .h 文件,然后是带有实际定义的 .cpp 文件。
      猜你喜欢
      • 2021-11-29
      • 2015-10-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-29
      • 1970-01-01
      • 2016-09-13
      • 2020-04-03
      相关资源
      最近更新 更多