C++ 将类代码分离为头文件和cpp文件

C++ 将类代码分离为头文件和cpp文件,c++,oop,class,C++,Oop,Class,我对如何将一个简单类的实现和声明代码分离到一个新的头文件和cpp文件感到困惑。例如,我将如何分离以下类的代码 class A2DD { private: int gx; int gy; public: A2DD(int x,int y) { gx = x; gy = y; } int getSum() { return gx + gy; } }; 基本上是函数声明/定义的修改语法: a2dd.h class A2DD { pr

我对如何将一个简单类的实现和声明代码分离到一个新的头文件和cpp文件感到困惑。例如,我将如何分离以下类的代码

class A2DD
{
  private:
  int gx;
  int gy;

  public:
  A2DD(int x,int y)
  {
    gx = x;
    gy = y;
  }

  int getSum()
  {
    return gx + gy;
  }
};

基本上是函数声明/定义的修改语法:

a2dd.h

class A2DD
{
private:
  int gx;
  int gy;

public:
  A2DD(int x,int y);

  int getSum();
};
a2dd.cpp

A2DD::A2DD(int x,int y)
{
  gx = x;
  gy = y;
}

int A2DD::getSum()
{
  return gx + gy;
}

将声明保留在头文件中:

class A2DD
{
  private:
  int gx;
  int gy;

  public:
    A2DD(int x,int y); // leave the declarations here
    int getSum();
};
并将定义放入实现文件中

A2DD::A2DD(int x,int y) // prefix the definitions with the class name
{
  gx = x;
  gy = y;
}

int A2DD::getSum()
{
  return gx + gy;
}
您可以将两者混合使用(例如,将
getSum()
定义保留在标题中)。这很有用,因为它为编译器提供了更好的内联机会。但这也意味着更改实现(如果保留在头中)可能会触发包含头的所有其他文件的重建

请注意,对于模板,您需要将其全部保留在标题中。

A2DD.h

class A2DD
{
  private:
  int gx;
  int gy;

  public:
  A2DD(int x,int y);

  int getSum();
};
A2DD.cpp

  A2DD::A2DD(int x,int y)
  {
    gx = x;
    gy = y;
  }

  int A2DD::getSum()
  {
    return gx + gy;
  }
其思想是将所有函数签名和成员保留在头文件中。
这将允许其他项目文件查看类的外观,而不必知道实现


除此之外,您还可以在实现中包含其他头文件,而不是头文件。这很重要,因为头文件中包含的任何头文件都将包含(继承)在包含头文件的任何其他文件中。

类声明将进入头文件。添加
#ifndef
包含防护非常重要,或者如果您在MS平台上,也可以使用
#pragma一次
。我也省略了私有,默认C++类成员是私有的。
// A2DD.h
#ifndef A2DD_H
#define A2DD_H

class A2DD
{
  int gx;
  int gy;

public:
  A2DD(int x,int y);
  int getSum();

};

#endif
CPP文件中有实现:

// A2DD.cpp
#include "A2DD.h"

A2DD::A2DD(int x,int y)
{
  gx = x;
  gy = y;
}

int A2DD::getSum()
{
  return gx + gy;
}

通常您只在头文件中放置声明和非常短的内联函数:

class A2DD
{
  private:
  int gx;
  int gy;

  public:
    A2DD(int x,int y); // leave the declarations here
    int getSum();
};
例如:

class A {
 public:
  A(); // only declaration in the .h unless only a short initialization list is used.

  inline int GetA() const {
    return a_;
  }

  void DoSomethingCoplex(); // only declaration
  private:
   int a_;
 };

通常,您的.h包含类定义,即所有数据和所有方法声明。你的情况是这样的:

A2DD.h:

class A2DD
{
  private:
  int gx;
  int gy;

  public:
  A2DD(int x,int y);    
  int getSum();
};
然后,您的.cpp包含如下方法的实现:

A2DD.cpp:

A2DD::A2DD(int x,int y)
{
  gx = x;
  gy = y;
}

int A2DD::getSum()
{
  return gx + gy;
}

在以更广泛的方式研究该主题时,有必要向遇到此问题的读者指出,在您只想将项目拆分为文件的情况下,不需要接受答案的过程。只有当您需要单个类的多个实现时才需要它。如果每个类的实现是一个,那么每个类只需要一个头文件就足够了

因此,从接受答案的示例中,仅需要这一部分:

#ifndef MYHEADER_H
#define MYHEADER_H

//Class goes here, full declaration AND implementation

#endif
#ifndef等预处理器定义允许多次使用它


注:一旦你意识到C/C++是“哑巴”并且“包含”仅仅是一种说“在这里转储此文本”的方式,这个主题就变得更清晰了。

我不会参考太多你的示例,因为它对于一般的答案来说非常简单(例如,它不包含模板函数,这会迫使你在标题上实现它们),我遵循的经验法则是

它有很多好处,因为您可以获得更快的编译时间和语法优势:

class->member
而不是
class.member


唯一的缺点是您需要支付额外的指针。

请记住,如果您正在进行模板编程,然后,您必须将所有内容都保存在.h文件中,以便编译器在编译时实例化正确的代码。头文件中是否包含
#ifndef
内容?这意味着包含头文件的所有文件都将“看到”私有成员。例如,如果你想发布一个lib及其头文件,你必须显示类的私有成员?不,这里有一个很棒的私有实现习惯用法:你可以用它来隐藏实现细节。措辞上有一些小的挑剔:“类声明进入头文件”。这确实是一个声明,但它也是一个定义,但由于后者包括前者,我宁愿说类定义进入头文件。在转换单元中,您有成员函数的定义,而不是类的定义。我同意你的看法,这可能值得一点小小的编辑?只需要几点注释:构造函数应该始终使用初始化列表,而不是在主体中设置成员。要获得一个好的简单解释,请参阅:至少在大多数地方,公共领域位于顶部也是一种习惯。它不会影响任何东西,但由于公共字段是类的文档,因此将其放在顶部是有意义的。@martiert将
public:
成员放在顶部可能会影响很多,如果用户根据此建议移动了成员,但成员之间存在顺序依赖关系,并且还不知道成员是按照声明的顺序初始化的;-)@这是真的。但话说回来,我们在编译时都会把警告当作错误,以及我们能想到的所有警告,对吗?这至少会告诉你,你把事情搞砸了,但是,是的,人们习惯于使用一些小警告,而忽略它们:(@martiert Good point,有点忘记了它会生成警告——如果大多数人都读过警告的话:-)我使用它们,并尝试将它们全部编码掉。一些是不可避免的——所以我说‘谢谢你的警告,但我知道我在做什么!’但为了避免日后的混乱,最好对大多数字段进行修复。在我看来,将公共字段置于顶部只是一种风格,不幸的是,太多人已经采用了这种风格。此外,您需要记住@martiert提到的一些事情。将私有成员和函数放在头文件中不会被视为泄露实现细节?@Jason,有点像。这些是必要的实施细节。例如,我必须知道一个类将在堆栈上消耗多少空间。函数实现对于其他编译单元来说是不必要的。您可以通过将“拆分”文件放入
.cpp
中来做到这一点吗,或者仅
.h
对于这种代码组织方法来说真的是“好”的吗?我认为有些项目拆分头文件和(单个)实现文件,以便它们可以