我喜欢将所有#includes放在我的头文件中,然后只在我的源文件中包含我的源文件头。什么是行业标准?我的方法有没有退缩?
答案 0 :(得分:63)
通常,您只想将最少的必要包含放入类头文件中,因为使用该标头的任何其他人也将被强制为#include
所有这些。在较大的项目中,这会导致构建速度降低,依赖性问题以及各种其他肮脏问题。
将头文件视为您的类的公共接口。除非他们必要才能使用该类,否则您不想让使用它的每个人都有额外的依赖关系。
将类实现中仅需要的任何内容移动到源文件中。对于标题中使用的其他类,只有#include
标题,如果您确实需要知道标题中的标题 - 其他任何内容和forward declaration就足够了。大多数情况下,您只需要继承的#include
个类,以及其对象是您的类的值成员的类。
This page有一个很好的总结。 (以下复制以供参考)
即使在C语言编程中,大型软件项目也需要仔细的头文件管理。当开发人员转向C ++时,头文件管理变得更加复杂和耗时。在这里,我们提供了一些头文件包含模式,这将简化这项工作。
在这里,我们讨论了简化头文件管理所需的C ++头文件包含的基本规则。
只有在前向声明不能完成工作时才应包含头文件。
头文件的设计应使头文件包含的顺序不重要。
这是通过确保x.h
是x.cpp
中的第一个头文件来实现的。
头文件包含机制应该容忍重复头文件包含。
以下部分将借助示例解释这些规则。
以下示例说明了不同类型的依赖项。假设A类的代码存储在a.cpp
和a.h
。
a.h
#ifndef _a_h_included_
#define _a_h_included_
#include "abase.h"
#include "b.h"
// Forward Declarations
class C;
class D;
class A : public ABase
{
B m_b;
C *m_c;
D *m_d;
public:
void SetC(C *c);
C *GetC() const;
void ModifyD(D *d);
};
#endif
a.cpp
#include "a.h"
#include "d.h"
void A::SetC(C* c)
{
m_c = c;
}
C* A::GetC() const
{
return m_c;
}
void A::ModifyD(D* d)
{
d->SetX(0);
d->SetY(0);
m_d = d;
}
让我们从本示例中涉及的类的角度分析头文件包含,即ABase
,A
,B
,C
和{{1 }}
D
是基类,因此需要类声明才能完成类声明。编译器需要知道ABase
的大小以确定ABase
的总大小。在这种情况下,A
中应明确包含abase.h
。a.h
按值包含类A
,因此需要类声明才能完成类声明。编译器需要知道B的大小以确定B
的总大小。在这种情况下,A
中应明确包含b.h
。a.h
仅作为指针引用包含在内。 Class C
的大小或实际内容对C
或a.h
并不重要。因此a.cpp
中只包含一个前瞻性声明。请注意,a.h
或c.h
中未包含a.h
。a.cpp
仅用作D
中的指针引用。因此,前瞻性声明就足够了。但a.h
实质上使用了类a.cpp
,因此明确包含D
。只有在前向声明无法完成工作时才应包含头文件。如果不包括d.h
和c.h
其他类d.h
的客户,则永远不必担心A
和c.h
,除非他们按值使用C类和D类。
d.h
中已包含a.h
作为第一个头文件。这将确保a.cpp
不期望在a.h
之前包含某些头文件。由于a.h
已作为第一个文件包含在内,因此a.h
的成功编译将确保a.cpp
不期望在a.h
之前包含任何其他头文件。
如果所有类都遵循此类(即a.h
始终包含x.cpp
作为第一个标头),则不会依赖于头文件包含。
x.h
包括检查符号a.h
的预处理器定义。这使得它可以容忍重复_a_h_included_
的包含。
以下示例中,类a.h
和X
之间存在循环依赖关系。这种依赖关系是通过使用前向声明来处理的。
Y
x.h and y.h