标题包括C ++中的蠕变

时间:2015-03-06 17:09:23

标签: c++

当我开始学习C ++时,我了解到头文件通常应该在其他头文件中包含#included。刚才有人告诉我,我应该在我的.cpp文件中包含一个特定的头文件,以避免头文件包含蠕变。

有人可以告诉我究竟是什么,为什么这是一个问题,并且可能指出一些关于何时我想在另一个标题中包含文件以及何时应该在.cpp文件中包含一个文件的文档?

5 个答案:

答案 0 :(得分:2)

  

有人可以告诉我究竟[包括蠕变]是什么

它不是一个编程术语,但在英语语境中解释它意味着它引入了#include语句并不是必需的。

  

为什么这是一个问题

因为编译代码需要时间。因此,编译不必要的代码会花费不必要的时间。

  

并且可能会指出一些关于何时我想在另一个头文件中包含文件以及何时应该在.cpp文件中包含一个文件的文档?

如果您的标头需要类型的定义,则需要包含定义该类型的标头。

#include "type.h"

struct TypeHolder
{
    Type t;
//  ^^^^ this value type requires the definition to know its size.
}

如果您的标题只需要某种类型的声明,则该定义是不必要的,包含也是如此。

class Type;     

struct TypeHolder
{
    Type * t;
//  ^^^^^^ this pointer type has the already-known size of a pointer,
//         so the definition is not required.
}

作为一个支持这种做法价值的轶事,我曾经被置于一个项目,其代码库需要一小时才能完全编译。并且更改标题通常会导致下一次编译的大部分或全部时间。

在适用的情况下向标题添加前向声明,而不是包括立即将完整编译时间减少到16分钟,并且更改标题通常不需要完全重建。

答案 1 :(得分:2)

“creep”是指包含一个包含许多其他标题的标题。这有一些不良后果:

  • 间接包含每个标头的每个源文件的趋势,因此对任何标头的更改都需要重新编译所有内容;
  • 更多可能导致心痛的循环依赖。

通常只需声明所需的类,而不是包含提供完整定义的标题,通常可以避免从另一个标题中包含一个标题。这样的不完整类型可以以各种方式使用:

// Don't need this
//#include "thingy.h"

// just this
class thingy;

class whatsit {

    // You can declare pointers and references
    thingy * p;
    thingy & r;

    // You can declare static member variables (and external globals)
    // They will need the header in the source file that defines them
    static thingy s;

    // You can declare (but not define) functions with incomplete
    // parameter or return types
    thingy f(thingy);
};

有些事情需要完整的定义:

// Do need this
#include "thingy.h"

// Needed for inheritance
class whatsit : thingy {

    // Needed for non-static member variables
    thingy t;

    // Needed to do many things like copying, accessing members, etc
    thingy f() {return t;}
};

答案 2 :(得分:1)

好吧,他们可能指的是一些事情(“包括蠕变”不是我以前听过的术语)。如果您作为一个极端规则,包含标题中的标题(除了每个源文件一个匹配的标题):

  • 编译时间增加,因为必须为所有源文件编译许多标头。
  • 不必要的依赖性增加,例如使用基于依赖的构建系统,修改一个头可能会导致不必要的重新编译许多其他文件,从而增加编译时间。
  • 增加了命名空间污染的可能性。
  • 循环依赖性成为一个问题(两个标题需要相互包含)。
  • 其他更高级的依赖关系相关主题(例如,这违背了opaque pointers的目的,这会在某些类型的应用程序中导致许多设计问题。)

作为一般的经验法则,只需要编译头文件所需的最少数量的东西来编译该头文件中的代码(也就是说,如果它是足够的话,允许头编译包括它自己,但不超过那个)。这将解决所有上述问题。

例如,如果您有一个在代码中使用std::list的类的源文件,但该类本身没有使用std::list的成员或函数参数,则没有理由{{ 1}}来自类的标题,因为类的标题中没有任何内容使用#include <list>。从源文件中实际执行,而不是在头文件中,使用该类的所有内容也必须不必要地编译std::list

另一种常见技术是前向声明指针类型。这是解决循环依赖问题的唯一真正方法,并且还具有上面列出的一些编译时间优势。例如,如果两个类彼此引用但仅通过指针引用:

<list>

然后在 source 文件中包含每个文件的标题,其中实际需要定义。

答案 3 :(得分:0)

您的头文件应包含在源文件中单独(或第一次)包含时需要编译的所有头文件。在许多情况下,您可以通过使用前向声明来允许标头编译,但是您应该从不依赖包含特定标头的某人在包含您的标头之前。

答案 4 :(得分:0)

theres有时间考虑,但也有依赖性。如果A.h包含B.h而反之,代码将无法编译。您可以通过向前重新分类您的类来解决这个问题,然后在cpp中包含标题

A.h

class B;
class A
{
    public:
    void CreateNewB():
    private:
    B* m_b;
};

A.cpp

#include "B.h"

A::CreateNewB()
{
   m_b = new B;
}