1

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

假设我想为一个相当复杂的物理系统建模。在我的理解中,对这个系统进行建模的一种方法是将它作为一个类来引入。但是,由于涉及到系统,类会很大,可能包含许多数据成员、成员函数和子类。将主程序和此类放在一个文件中会非常混乱,因此为了更好地了解项目,我倾向于将类放在单独的 .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 文件的预期用途吗?

4

4 回答 4

6

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

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

于 2021-03-08T11:15:29.263 回答
2

添加到其他答案:

这是一个函数定义

double myfunction()
{
    return foo + bar;
}

这是一个函数声明

double myfunction();

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

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

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

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

于 2021-03-08T11:32:43.927 回答
1

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

我的类.hpp

#ifndef MY_CLASS_H
#define MY_CLASS_H

class myclass
{
public:
    double myfunction( );
private: 
    double foo;
    double bar;
};

#endif

我的类.cpp

#include "myclass.hpp"

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

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

#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

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

#ifndef MY_CLASS_H
#define MY_CLASS_H

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

#endif
于 2021-03-08T11:34:55.967 回答
0

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

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