如果用C ++在单个文件中编写整个类,会发生什么样的坏事?

时间:2010-03-19 18:10:34

标签: c++ class

在C#或Java中,同时声明和定义类。在C ++中,规范是分开执行。如果我们将整个类写在一个文件中,例如.cpp文件并将其包含在引用它的文件中,除了延长的编译过程之外,技术上会发生什么样的坏事呢?

8 个答案:

答案 0 :(得分:12)

如果MyClass的实施完全在标题文件MyClass.h中,则只要有人包含MyClass,就会包含您需要实施MyClass.h的任何文件。

如果更改MyClass.h的任何部分,即使它是微不足道的(例如添加注释甚至是空格),那么包含它的所有文件都必须重新编译,即使接口没有更改。

这些都不适用于玩具项目,但正如您所指出的,当您拥有一个由数百(或数千个等)类文件组成的程序时,单独添加的编译时间使得将实现与接口分离是值得的

例如,如果我有以下内容:

// MyClass.h
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>

#include "Inventory.h"

class MyClass
{
public:
  MyClass();

  void processInventory(Inventory& inventory)
  {
    // Do something with each item in the inventory here
    // that uses iostream, iomanip, sstream, and string
  }
private:
  // ...
};

它会更加理想地写成:

// MyClass.h
class Inventory;

class MyClass
{
public:
  MyClass();

  void processInventory(Inventory& inventory);
private:
  // ...
};

// MyClass.cc
#include "MyClass.h"

#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>

#include "Inventory.h"

MyClass()::MyClass()
{
}

void MyClass()::processInventory(Inventory& inventory)
{
  // Do something with each item in the inventory here
  // that uses iostream, iomanip, sstream, and string
}

注意:包含MyClass.h并不意味着必须解析iostreamiomanipsstreamstringInventory.h。更改processInventory的工作方式并不意味着必须重新编译使用MyClass.h的所有文件。

注意现在弄清楚如何使用MyClass会更容易。头文件有一个重要目的:它们向人们展示如何使用你的课程。使用修改后的MyClass.h,可以轻松查看功能列表。如果在标题中定义了每个函数,那么您不能仅查看函数列表。这使得弄清楚如何使用该类更加困难。

答案 1 :(得分:5)

您可以打破one definition rule

如果你这样写:

class foo
{
public:
    void doit();
};

foo::doit() {}

并将其包含在多个类中,您将拥有foo::doit的多个定义,并且您的链接将失败。

但是如果你把你的所有类都内联,可以在类声明中定义它们:

class foo
{
public:
    void doit() {
    }
};

或明确地将它们内联:

class foo
{
public:
    void doit();
};

inline void foo::doit() {}

然后您可以根据需要多次包含该文件。

答案 2 :(得分:2)

当您尝试组合多个此类对象时,链接器将看到该类成员的多个定义。因此,您将无法从包含多个位置的任何内容的源文件生成二进制文件。

答案 3 :(得分:1)

通常,您将类的声明和定义分开。这允许您通过简单地包含声明来在不同的源文件中使用您的类。

如果你将包含声明和定义的.cpp包含在2个不同的源文件中,那么该类将被双重定义。

包含该类的每个.cpp都可以很好地编译到目标文件中。但是,每个类必须只有1个定义,否则您将无法将目标文件链接在一起。

答案 4 :(得分:1)

与其他语言导入方法形成对比的#include最重要的一点是#include COPIES 放置#include指令的文件的内容。因此,在同一文件中声明和定义类将创建三件事:

  • 显着增加你的编译 次。

  • 如果您的定义不是内联的 你会得到链接器错误,因为 编译器找到多个
    对相同功能的定义

  • 这会暴露实施 对用户而不仅仅是界面。

这就是为什么通常的做法是在单独的文件中定义大型类,在某些情况下,在一个文件中使用小实现(如智能指针)的小型类(也隐式内联方法)。

答案 5 :(得分:1)

@Bill

我认为重要的是要强调比尔的观点:

  

注意它有多容易   弄清楚如何使用MyClass。   头文件很重要   目的:他们向人们展示如何使用   你的班级。

<。> .h文件或多或少是“公共”文档,以便在概念上理解你的类在某些方面是如何工作的 - 一个接口。请记住,源文件应该被认为是专有的。我记得通过读取头文件,在我早期的C / C ++时代学到了很多关于Unix如何工作的知识。还要记住,内联函数的复杂性应该只不过是访问者的

答案 6 :(得分:0)

在cpp文件中定义类的一个重要原因是它不需要公开,它只是一个辅助函数(比如一个函子)。有些人似乎害怕将完整的类放在cpp文件中,而这只是表明你只想在那里使用类的意图。

答案 7 :(得分:0)

文件通常是版本控制系统的原子 - 如果您将事物明智地划分到不同的文件中,那么开发人员团队就可以只检查他们每个人需要处理的部分。将所有内容放在一个文件中,你就不能这样做。