在.h文件或.cpp文件中实现类之间的区别

Jac*_*ack 31 c++ header class

我想知道哪些是仅在头文件中声明和实现类之间的区别,与在标头中对类进行类型化并在有效的.cpp文件中实现的常规方法相比.

为了更好地解释我在说什么,我的意思是正常方法之间的区别:

// File class.h
class MyClass 
{
private:
  //attributes
  public:
  void method1(...);
  void method2(...);
  ...
};

//file class.cpp
#include "class.h"

void MyClass::method1(...) 
{
  //implementation
}

void MyClass::method2(...) 
{
  //implementation
}
Run Code Online (Sandbox Code Playgroud)

和一个公正的方法:

// File class.h
class MyClass 
{
private:
  //attributes
public:
  void method1(...) 
  {
      //implementation
  }

  void method2(...) 
  {
    //implementation
  }

  ...
};
Run Code Online (Sandbox Code Playgroud)

我可以得到主要区别:在第二种情况下,代码包含在每个需要它生成相同实现的更多实例的其他文件中,因此是隐式冗余; 而在第一种情况下,代码是由自己编译的,然后每个引用对象的调用MyClass都链接到实现中class.cpp.

但还有其他差异吗?取决于具体情况,使用方法而不是另一种方法更方便吗?我还读过某个地方,直接将方法的主体定义到头文件中是对编译器内联该方法的隐式请求,是真的吗?

Ste*_*sop 36

主要的实际区别在于,如果成员函数定义在标题的主体中,那么它们当然会为包含该标题的每个翻译单元编译一次.当您的项目包含几百或几千个源文件,并且相关的类被广泛使用时,这可能意味着大量的重复.即使每个类仅由2个或3个其他类使用,标题中的代码越多,工作量就越多.

如果成员函数定义在它们自己的转换单元(.cpp文件)中,则它们被编译一次,并且只有函数声明被多次编译.

确实,类定义中定义的成员函数(不仅仅是声明的)是隐式的inline.但这inline并不意味着人们可能合理地猜测它意味着什么.inline他说,函数的多个定义出现在不同的翻译单元中是合法的,后来被链接在一起.如果类位于不同源文件将要使用的头文件中,则必须执行此操作,因此该语言会尝试提供帮助.

inline也是对编译器的暗示,该函数可以有用地内联,但尽管名称,这是可选的.编译器越复杂,就越能够自己做出关于内联的决定,并且对提示的需求就越少.比实际的内联标记更重要的是该函数是否完全可用于编译器.如果函数是在不同的转换单元中定义的,那么在编译对它的调用时它就不可用,因此如果有任何内容要内联调用,则它必须是链接器,而不是编译器.

通过考虑第三种可能的方法,您可能能够更好地看到差异:

// File class.h
class MyClass
{
    private:
        //attributes
    public:
       void method1(...);
       void method2(...);
       ...
};

inline void MyClass::method1(...)
{
     //implementation
}

inline void MyClass::method2(...)
{
     //implementation
}
Run Code Online (Sandbox Code Playgroud)

既然隐式内联已经不在了,那么这个"all header"方法和"header plus source"方法之间仍然存在一些差异.如何在翻译单元之间划分代码会对构建时发生的事情产生影响.


Ben*_*n S 7

对包含实现的标头的任何更改都将强制包含该标头的所有其他类重新编译和重新链接.

由于标头的更改频率低于实现,因此通过将实现放在单独的文件中,可以节省大量的编译时间.

正如其他一些答案已经指出的那样,是的,在文件class块中定义方法将导致编译器内联.


Nav*_*een 7

是的,编译器将尝试内联直接在头文件中声明的方法,如:

class A
{
 public:
   void method()
   {
   }
};
Run Code Online (Sandbox Code Playgroud)

在分离头文件中的实现时,我可以考虑以下方便:

  1. 由于相同的代码包含在多个翻译单元中,因此您不会遇到代码膨胀
  2. 您的编译时间将大大减少.请记住,对于头文件中的任何修改,编译器必须构建直接或间接包含它的所有其他文件.我想对于任何人来说再次构建整个二进制文件只是为了在头文件中添加一个空格将是非常令人沮丧的.