困惑于C++中头文件的实际用途

18

可能是重复内容:
为什么C++中要有头文件和cpp文件?

我对C++头文件的作用有些疑惑,存在两个相互冲突的原因:

  1. 我认为头文件的目的通常是将接口和实现分离。换句话说,客户端使用头文件来了解如何使用类,但不必担心类如何在内部实现此功能。

  2. 那么,为什么C++类的私有变量要在它的头文件中指定呢?

在我看来,在头文件中具有私有变量违反了接口和实现的分离。直觉上,我认为将私有变量放在源文件中会更有意义,因为这个文件不会暴露给外部世界。

也许我只是误解了头文件的用途,1.上述理解是完全错误的吗?哪些方面是这样的?


除了“友元”函数之外,我认为这只是编译器确定类大小的技术限制。 - Mysticial
@DaveSwersky 这个重复问题并没有回答楼主的第二个问题:“为什么C++类的私有变量要在头文件中声明?” - Mysticial
12
@NoobOverflow,你的第一点说得对。第二个问题也很好。答案是,在大多数情况下,C++的编译模型很糟糕。 - R. Martinho Fernandes
1
2所以代码使用类知道类的大小,因此它知道为在堆栈上创建的实例留下多少空间。 - Rup
只是说,在模块化(导入/导出)系统中,您需要在头文件(=源代码)中包含所有私有信息。 - apple apple
显示剩余3条评论
4个回答

13
C++头文件的主要功能是在C++中,没有像其他语言一样的模块导入或类似的东西。编译器了解其他文件中的类型、函数等唯一的方法是通过使用#include将代码粘贴到当前源文件中。
从理论上讲,您可以将所有源代码都放在头文件中,并只有一个源文件包含所有头文件。之所以通常不这样做,原因有两个。首先,编译时间会更长(某些项目中很重要),任何文件的更改都将导致整个项目重新编译。其次,将实现放入源文件中确实有助于将接口与实现分离,即使部分实现仍然在头文件中指定。
请注意,在头文件中的内联方法也存在向公共/客户端暴露实现细节的风险。
如果您真的希望完全将接口与实现分开(这确实具有优势),则使用pimpl惯用语是C++的一种方式。使用该惯用语,所有私有数据都被隐藏在源文件中,只提供一个抽象接口给公众使用。此外,使用非虚拟接口(NVI)模式可以进一步帮助隔离客户端不受接口更改的影响。

10

C++将函数和类的声明与定义分开。通常,C++头文件包含类的声明。由于不允许部分声明,因此头文件需要包含完整的类声明,包括所有私有成员(变量和成员函数)。

如果您想要隐藏完整的实现细节,可以使用“pimpl习语”来实现此目的。pimpl idiom


7
头文件的目的是为了让编译器获取共享定义的信息,以便在编译单元(.cpp源文件)之间进行共享。这是一种机械性的操作,而不是一种哲学上的操作。
例如,私有成员变量需要被声明,因为它们定义了一个对象的大小,当编译器分配一个对象时,它需要知道大小。

“在编译单元之间‘共享声明’的术语不是正确的术语吗?声明和定义的含义经常被混淆…” - πάντα ῥεῖ
@g-makulik,我使用“definitions”这个词的英语意义,而不是C++中的正式用法。并且在头文件中包含定义是完全可能的,例如对于模板来说几乎是必须的。 - Mark Ransom
为什么只使用私有成员变量? - stdout
@zgulser,不仅限于私有成员变量,那只是一个例子。整个声明都是必需的。 - Mark Ransom
@MarkRansom 谢谢Mark。实际上,我很惊讶除了你之外没有人提到分配(但你提到了),因为我认为这是C++不得不将对象状态放在头文件中以便在堆栈上进行分配的主要原因。我猜那时候这是他们能想到的唯一方法。虽然Java把所有东西都放在一个单一的容器中,但它把它搞得更糟了。 - stdout

0
在C/C++中,头文件的一个目的是允许多个翻译单元使用具有相同定义的类型,而不需要在多个文件中重复定义。类的定义的一部分是其私有成员。

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