我应该按什么顺序包含头文件?

7

我刚开始学编程,头文件这个话题让我感到有些困惑,因为我使用了很多头文件。此外,我正在尝试使用预编译头文件。我还在使用SFML库,所以必须包含那些头文件。

现在,我有stdafx.h、main.cpp,以及在A.h、A.cpp、B.h、B.cpp、C.h、C.cpp、D.h和D.cpp中包含的类A、B、C和D。

如果:

  • 所有类都包含一个SFML类的实例
  • D类包含A类和C类的实例
  • C类包含B类的实例 我的代码如下:(注意:所有头文件都有头部保护)

stdafx.h:

#include <SFML/Graphics.hpp>
#include <iostream>

A.h

#include "stdafx.h"
class A
{
    //sfml class
};

A.cpp

#include "stdafx.h"
#include "A.h"

B.h

#include "stdafx.h"
class B
{
    //sfml class
};

B.cpp

#include "stdafx.h"
#include "B.h"

C.h

#include "B.h"
class C: public B
{

};

C.cpp

#include "stdafx.h"
#include "C.h"

D.h

#include "A.h"
#include "C.h"
class D
{
    A a;
    C C; // if left uncommented I recieve a '1 unresolved externals' error
    //sfml class
}

D.cpp

#include "stdafx.h"
#include "D.h"

main.cpp

#include "stdafx.h"
#include "D.h"

main.cpp 是否使用了类 A,B,C,D 中的任何一个? - Mahesh
有时候这样想会很有帮助:当预处理器遍历你的文件时,它会将所有包含的头文件和 cpp 源文件放在一个大文件中,然后进行编译。所以想象一下你的程序在一个大文件中,然后考虑什么顺序是有意义的。 - WildCrustacean
我通常按照类别的字母顺序从高到低包含它们:核心(即标准)、平台(例如Qt、Windows)、第三方库,然后是我的程序。使用包含保护符时,顺序并不重要,但这样做可以更容易地看到有什么以及在使用类时是否需要添加某些内容。我还始终包括文件中使用的所有内容,即使我知道它已经在某个头文件中包含了:头文件可能会发生变化,即使很少,这也使我的意图更清晰。 - user439793
5个回答

10
我的理念是在编写良好的代码时,头文件应包含它所依赖的所有其他头文件。我的思路是不应该可能包含一个头文件并因此得到编译器错误。因此,每个头文件应该(在#ifdef#pragma once 包含守卫之后)包含其所依赖的所有其他头文件。
为了非正式地测试您是否已经记住要在头文件中包含正确的头文件,*.cpp文件应该 #include 最小化的能工作的头文件集合。 因此,如果有单独的头文件ABCD,而您的cpp文件使用类D,那么它应该只包含D.h。 不应该出现编译器错误,因为D.h包括A.hC.hC.h包括B.h,而A.hB.h包括SFML头文件(无论那是什么)。 如果适当的话,C.hD.h可以包含SFML头文件,但如果您可以确定依赖项(B.hA.h)已经包含它,则这不是必要的。
然而,Visual C++的“预编译头文件”机制破坏了这个逻辑。它要求您将"StdAfx.h"作为第一个头文件进行包含,这导致许多开发人员简单地将整个项目的所有#includes放在StdAfx.h中,并且不在其他任何头文件中使用#include。我不推荐这样做。或者,他们会将所有外部依赖项放在StdAfx.h中(例如windows.h、boost头文件),并在其他地方#include本地依赖项,以便更改单个头文件不一定会导致整个项目重新构建。

我的代码编写方式是,大部分CPP文件都包含StdAfx.h和对应的.H文件。因此,A.cpp包括StdAfx.h和A.h,B.cpp包括StdAfx.h和B.h,以此类推。cpp文件中唯一的其他#include是“内部”依赖项,这些依赖项不会通过头文件公开。例如,如果类A调用printf(),则A.cpp(而不是A.h)会#include <stdio.h>,因为A.h不依赖于stdio.h。

如果按照这些规则编写代码,则#include头文件的顺序是无关紧要的(除非您使用预编译头文件:此时预编译头文件在每个cpp文件中位于第一位,但不需要从头文件中包含它)。


我相信我正确地遵循了您的答案,但是当D.h包含类C的实例时,我收到以下错误: fatal error LNK1120: 1 unresolved externals 当该实例被注释掉时,代码编译得非常好。 - orod
它声称未解决的是哪个外部问题?你的示例中没有方法或全局变量。类本身不会被链接器处理(链接器主要处理函数和静态/全局变量),因此链接器在上面发布的代码中无可抱怨的地方。 - Qwertie

1

C 继承类 B。因此,它应该看到标识符 B。所以,在这里包含 B.h -

#include "B.h"    // Newly added
// Or you can forward declare class B ; 
class C: public B
{

};

D有类AB的对象。所以,在"D.h"本身中包含AB的头文件。

class D
{
    A a;  // Should see the definition of class A
    C c;  // Should see the definition of class B
    //sfml class
}

D.cpp

#include "A.h"
#include "C.h"
#include "D.h"  // Notice that A.h and C.h should definitely placed before

请注意每个头文件都需要包含在其对应的源文件中。将每个源文件视为独立的,查看所使用的任何内容是否在源文件中早已定义。

由于某些原因,D.h文件无法看到C类的定义。 - orod
如果包含指向类C的指针,则奇怪的是D会编译通过,但如果不是指针,则会出现未解决的外部错误。噢,好吧,我只需绕过这个问题。 - orod

1

看一下类似的问题,学习编写头文件的好方法。

简而言之,您需要在定义守卫内定义每个头文件,以防止在编译期间多次包含头文件。有了这些,对于每个.h和.cpp文件,只需包含解析任何声明所需的头文件即可。预处理器和编译器会处理其余部分。


stdafx.h 包含了 SFML 头文件,A.h、B.h、C.h 和 D.h。我的所有头文件都有头文件保护,并且只包含 stdafx.h。我的所有源文件也只包含 stdafx.h。现在每当我编译时,我会得到大量的“缺少类型”错误,我认为这是因为每个头文件在编译时只被包含一次,所以使用该头文件的下一个类不会将其包含进来。这就是为什么我问应该按什么顺序包含头文件的原因。我正在使用 Microsoft Visual C++ 2010 Express Edition。 - orod

1

A.h 应该包含SFML

A.cpp 应该包含A.h

D.h 应该包含SFML、A.h和C.h

D.cpp 应该包含D.h

main.cpp 应该直接包含A、B、C、D和SFML中使用的任何一个。

通常在 .cpp 文件中,我不会包含任何你知道必须由相应的 .h 包含的头文件,因为它们包含了在该 .h 中定义的类的数据成员的定义。因此,在我的代码中,D.cpp 不会包含 A.h。这只是我的做法,但你可能更喜欢包含它,以提醒你该 .cpp 文件(可能)使用了它。

这就留下了 stdafx - 你需要它取决于它所使用的内容。可能它在任何地方都需要,并且 MSVC 不会处理(或者处理但会丢弃?)源文件中 #include "stdafx.h" 之前的任何内容,因此它应该是每个 .cpp 文件中的第一件事,并且不出现在其他任何地方。

所有的头文件都应该有多重包含保护。

你可以将SFML(或其他任何你想要的东西)添加到 stdafx.h 中,这样你就可以从其他任何地方删除这些包含。

完成这个步骤后,每个文件中包含头文件的顺序就不再重要了。所以你可以随意操作,但我建议参考Google的C++编程风格指南(点击箭头),并根据stdafx.h进行调整。


0

取决于依赖关系。与C#和其他类似的语言不同,C++按照编写顺序执行操作,因此可能会出现问题。如果您在顺序方面遇到问题,则无法编译。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接