简体   繁体   English

在实现(.cpp)文件中不包括相应的头文件(.h)仍可以编译吗?

[英]Not including the corresponding header (.h) file in the implementation (.cpp) file still compiles?

I wrote up a quick example today just to see if it would compile and I was actually quite surprised when I found that it did! 我今天写了一个简单的示例,只是为了看看它是否可以编译,当我发现它可以编译时,我实际上感到非常惊讶!

Here is the example: 这是示例:

hello.h hello.h

#ifndef HELLO_H
#define HELLO_H

// Function prototype
void say_hello();

#endif

hello.cpp HELLO.CPP

NOTE: This does NOT include "hello.h" like it would in every C++ example I have ever seen in the history of forever! 注意:这不包括“ hello.h”,就像我在永远的历史中见过的每个C ++示例一样!

// #include "hello.h" <-- Commented out. The corresponding header is NOT included.

#include <iostream>

void say_hello() {
    std::cout << "Hello!" << std::endl;
}

main.cpp main.cpp中

#include "hello.h"

int main() {
    say_hello();
}

I then compiled "hello.cpp" into a static library as follows: 然后,我将“ hello.cpp”编译为静态库,如下所示:

g++ -c hello.cpp
ar -rvs libhello.a hello.o

Then I compiled the "main" application and linked it to the library 然后,我编译了“主”应用程序并将其链接到库

g++ -o main main.cpp -L. -lhello

And ran it and it executed fine! 并运行它,它执行得很好!

./main

Hello! 你好!


While I was surprised... I do understand why this works. 当我感到惊讶的时候...我确实理解为什么这样做。 It is because the function in "hello.cpp" is not declared static so it has external linkage and can be seen from outside. 这是因为“ hello.cpp”中的函数未声明为静态,因此具有外部链接并且可以从外部看到。 Making it static will cause the link to fail due to an undefined reference. 将其设置为静态将由于未定义引用而导致链接失败。

So here's the question... If this does work, then why does everyone everywhere ALWAYS include the ".h" header file with the function declarations in the ".cpp" implementation file. 因此,这就是问题……如果这行得通,那么为什么每个地方的人总是在“ .cpp”实现文件中包括“ .h”头文件和函数声明。 Clearly if it is just defining free functions, this is not necessary and everything will work fine if the header file is not included. 显然,如果仅定义自由函数,则没有必要,并且如果不包含头文件,一切都可以正常工作。

So why do we always include it? 那么为什么我们总是将其包括在内? -- Is it simply a general lack of understanding of how the linker works? -是否只是对链接器的工作原理普遍缺乏了解? Or is there something more? 还是还有更多?

Let us change your hello.cpp : 让我们更改您的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;
}

This will compile just as well as the previous version. 这将与以前的版本一样编译。 It will probably link too - but it isn't right. 它也可能会链接-但不正确。 The return type is wrong. 返回类型错误。

This is undefined behaviour, but in many common implementations, you will get away with it because you don't use the return value, and it is often returned in a register. 这是未定义的行为,但是在许多常见的实现中,您将不使用它,因为您不使用返回值,并且通常在寄存器中返回它。 However, it doesn't have to be - and you may get very strange errors at run time. 但是,不必一定如此-您在运行时可能会遇到非常奇怪的错误。 Particularly if the difference is a bit more complicated (like returning double when the callers expect int - that will often be returned in a different register). 特别是如果差异稍微复杂一些(例如,当调用方期望int时返回double -通常会在其他寄存器中返回)。

If on the other hand, you had written: 另一方面,如果您写了:

#include "hello.h"

#include <iostream>

int say_hello() {
    std::cout << "Hello!" << std::endl;
    return 0;
}

Then the declaration in the header file would not have matched the definition in the CPP file - and you would have got a nice, easy to understand, compiler error message. 然后,头文件中的声明将与CPP文件中的定义不匹配-并且您将获得一个不错的,易于理解的编译器错误消息。

In fact, this is such a good idea that GCC will complain if you don't have a declaration of an external function. 实际上,这是一个好主意,如果您没有外部函数的声明,GCC会抱怨。 (And if you have -wall -werror on your command line, it will stop your build.) (如果您在命令行上使用-wall -werror,它将停止您的构建。)

If you have a class, you'll want to include it to get the declaration of the class and its members for their definitions to match. 如果您有一个类,则需要包含它以获取该类及其成员的声明,以使其定义匹配。 Otherwise, you won't be able to separate the definition and declaration. 否则,您将无法分开定义和声明。

/// C.h
class C
{
public:
    C();
private:
    int _i;
};
/// C.cpp
// #include "C.h"

C::C() : _i(42) {} // error: 'C' does not name a type

See it fail on Coliru . 看到它在Coliru上失败了。

Likewise, if you have a class template or a function template, it usually needs to be in a header so that versions of it can be stamped out later. 同样,如果您具有类模板或函数模板,则通常需要将其放在标头中,以便以后可以将其版本标记出来。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM