当我开始学习C ++时,我了解到头文件通常应该在其他头文件中包含#included。刚才有人告诉我,我应该在我的.cpp文件中包含一个特定的头文件,以避免头文件包含蠕变。
有人可以告诉我究竟是什么,为什么这是一个问题,并且可能指出一些关于何时我想在另一个标题中包含文件以及何时应该在.cpp文件中包含一个文件的文档?
答案 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)
好吧,他们可能指的是一些事情(“包括蠕变”不是我以前听过的术语)。如果您作为一个极端规则,仅包含标题中的标题(除了每个源文件一个匹配的标题):
作为一般的经验法则,只需要编译头文件所需的最少数量的东西来编译该头文件中的代码(也就是说,如果它是足够的话,允许头编译包括它自己,但不超过那个)。这将解决所有上述问题。
例如,如果您有一个在代码中使用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;
}