当然,只有标题就可以了(只要您考虑已经提到的单一定义规则)。
您也可以编写没有任何头文件的 C++ 源代码。
严格来说,标头只不过是归档的源代码片段,可能是#include
d(即粘贴)到多个 C++ 源文件(即翻译单元)中。记住这个基本事实有时对我很有帮助。
我做了以下人为的反例:
main.cc
:
#include <iostream>
// define float
float aFloat = 123.0;
// make it extern
extern float aFloat;
/* This should be include from a header
* but instead I prevent the pre-processor usage
* and simply do it by myself.
*/
extern void printADouble();
int main()
{
std::cout << "printADouble(): ";
printADouble();
std::cout << "\n"
"Surprised? :-)\n";
return 0;
}
printADouble.cc
:
/* This should be include from a header
* but instead I prevent the pre-processor usage
* and simply do it by myself.
*
* This is intentionally of wrong type
* (to show how it can be done wrong).
*/
// use extern aFloat
extern double aFloat;
// make it extern
extern void printADouble();
void printADouble()
{
std::cout << aFloat;
}
希望你已经注意到我声明了
-
extern float aFloat
in main.cc
-
extern double aFloat
in printADouble.cc
这是一场灾难。
编译时出现问题main.cc
?不会。翻译单元在语法和语义上(对于编译器)是一致的。
编译时出现问题printADouble.cc
?不会。翻译单元在语法和语义上(对于编译器)是一致的。
将这些乱七八糟的东西连接在一起时出现问题吗?不会。链接器可以解析每个需要的符号。
Output:
printADouble(): 5.55042e-315
Surprised? :-)
正如预期的那样(假设你和我都没有任何理智的预期)。
wandbox 上的现场演示
printADouble()
访问了定义的float
变量(4字节)为double
变量(8 个字节)。这是未定义的行为,并且在多个层面上都会出错。
因此,使用标头不支持但支持 C++ 中的(某种)模块化编程。 (直到我曾经不得不使用还没有预处理器的 C 编译器时,我才意识到其中的区别。所以,上面概述的这个问题对我来说非常打击,但对我来说也确实很有启发。)
恕我直言,头文件是模块化编程基本功能的实用替代品(即接口的显式定义以及接口和实现作为语言功能的分离)。这似乎也惹恼了其他人。看一下关于 C++ 模块的几句话看看我的意思。