首页 > 解决方案 > 在实现 (.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”头文件。显然,如果它只是定义自由函数,这不是必需的,如果不包含头文件,一切都会正常工作。

那么为什么我们总是包含它呢?——这仅仅是对链接器如何工作的普遍缺乏了解吗?或者还有更多的东西?

标签: c++linkerheader-files

解决方案


让我们改变你的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,它会停止你的构建。)


推荐阅读