c++ - 在实现 (.cpp) 文件中不包括相应的头 (.h) 文件仍然可以编译吗?
问题描述
我今天写了一个简单的例子来看看它是否可以编译,当我发现它可以编译时,我真的很惊讶!
这是示例:
你好.h
#ifndef HELLO_H
#define HELLO_H
// Function prototype
void say_hello();
#endif
你好.cpp
注意:这不包括“hello.h”,就像我在永远的历史上见过的每个 C++ 示例一样!
// #include "hello.h" <-- Commented out. The corresponding header is NOT included.
#include <iostream>
void say_hello() {
std::cout << "Hello!" << std::endl;
}
主文件
#include "hello.h"
int main() {
say_hello();
}
然后我将“hello.cpp”编译成一个静态库,如下所示:
g++ -c hello.cpp
ar -rvs libhello.a hello.o
然后我编译了“主”应用程序并将其链接到库
g++ -o main main.cpp -L. -lhello
并运行它,它执行得很好!
./main
你好!
虽然我很惊讶......我确实理解为什么会这样。这是因为“hello.cpp”中的函数没有声明为静态的,所以它具有外部链接,可以从外部看到。将其设为静态将导致链接由于未定义的引用而失败。
所以这里的问题是......如果这确实有效,那么为什么每个地方的每个人都总是在“.cpp”实现文件中包含带有函数声明的“.h”头文件。显然,如果它只是定义自由函数,这不是必需的,如果不包含头文件,一切都会正常工作。
那么为什么我们总是包含它呢?——这仅仅是对链接器如何工作的普遍缺乏了解吗?或者还有更多的东西?
解决方案
让我们改变你的hello.cpp
:
// #include "hello.h" <-- Commented out. The corresponding header is NOT included.
#include <iostream>
int say_hello() {
std::cout << "Hello!" << std::endl;
return 0;
}
这将像以前的版本一样编译。它也可能会链接 - 但它是不正确的。返回类型错误。
这是未定义的行为,但在许多常见的实现中,您将侥幸逃脱,因为您不使用返回值,并且它通常在寄存器中返回。但是,它不一定是 - 您可能会在运行时遇到非常奇怪的错误。特别是如果差异有点复杂(比如double
在调用者期望时返回int
- 这通常会在不同的寄存器中返回)。
另一方面,如果你写过:
#include "hello.h"
#include <iostream>
int say_hello() {
std::cout << "Hello!" << std::endl;
return 0;
}
那么头文件中的声明将与 CPP 文件中的定义不匹配 - 您将得到一个很好的、易于理解的编译器错误消息。
事实上,这是一个好主意,如果您没有声明外部函数,GCC 会抱怨。(如果你的命令行上有 -wall -werror,它会停止你的构建。)
推荐阅读
- r - 在线图 r 中绘制分组变量
- excel - 使用 VBA 将单词与数字分开
- java - 如何从左到上更改 Android TV 应用中标题的位置?
- android - 如何使用向量打印文件中的单词列表,每次用户查询 [C++] 一次?
- powershell - ms access.exe挂起powershell时发送电子邮件
- sql - SQL 嵌套 IIF 函数
- cassandra - cassandra-reaper:维修一再推迟和卡住
- android - AdMob 永远不会在某些设备上加载
- google-maps - 谷歌静态地图不显示超过 5 个图钉
- sql - SQL从两个表中选择数据并按点排序