我从未真正理解为什么c++需要一个单独的头文件,其中包含与.cpp文件中相同的函数。它使得创建类和重构类变得非常困难,并且给项目添加了不必要的文件。还有一个必须包含头文件的问题,但是必须显式地检查它是否已经被包含。

c++在1998年被批准,那么为什么要这样设计呢?使用单独的头文件有什么好处?


后续问题:

当我所包含的是.h文件时,编译器如何找到其中的代码的.cpp文件?它是假定.cpp文件与.h文件具有相同的名称,还是实际上遍历目录树中的所有文件?


当前回答

你可以在没有头文件的情况下完美地开发c++。事实上,一些大量使用模板的库并不使用头文件/代码文件范例(参见boost)。但是在C/ c++中,你不能使用没有声明的东西。一个实用的方法 处理这个问题的方法就是使用头文件。此外,您还可以在不共享代码/实现的情况下共享接口。我认为这不是C语言的创造者所能想象到的:当你使用共享头文件时,你必须使用著名的:

#ifndef MY_HEADER_SWEET_GUARDIAN
#define MY_HEADER_SWEET_GUARDIAN

// [...]
// my header
// [...]

#endif // MY_HEADER_SWEET_GUARDIAN

这并不是真正的语言特性,而是处理多重包含的一种实用方法。

所以,我认为在创建C时,前向声明的问题被低估了,现在当使用像c++这样的高级语言时,我们必须处理这类事情。

对于我们这些可怜的c++用户来说,这又是一个负担……

其他回答

如果你想让编译器自动找到其他文件中定义的符号,你需要强迫程序员把这些文件放在预定义的位置(就像Java包的结构决定了项目的文件夹结构)。我更喜欢头文件。此外,你还需要你所使用的库的源代码,或者一些统一的方法来把编译器需要的信息放在二进制文件中。

c++的设计目的是在C基础设施中添加现代编程语言的特性,而无需不必要地更改C语言中与语言本身无关的任何内容。

是的,在这一点上(在第一个c++标准诞生10年之后,在它开始大量使用20年之后),很容易会问为什么它没有一个合适的模块系统。很明显,现在设计的任何新语言都不能像c++那样工作。但这不是c++的意义所在。

c++的重点是进化,是现有实践的平稳延续,只是添加新的功能,而不(经常)破坏对用户社区来说足够有效的东西。

这意味着,与其他语言相比,它使一些事情变得更困难(特别是对于开始一个新项目的人),而另一些事情则更容易(特别是对于维护现有代码的人)。

因此,与其期待c++变成c#(这毫无意义,因为我们已经有了c#),为什么不选择正确的工具来完成这项工作呢?就我自己而言,我努力用现代语言(我碰巧使用c#)编写大量的新功能,并且我有大量现有的c++,我将它们保存在c++中,因为重新编写它们将没有真正的价值。它们融合得很好,所以基本上是无痛的。

以我的理解(有限的-我通常不是一个C开发人员),这是植根于C。记住,C不知道类或名称空间是什么,它只是一个很长的程序。另外,在使用函数之前必须声明它们。

例如,下面应该给出一个编译器错误:

void SomeFunction() {
    SomeOtherFunction();
}

void SomeOtherFunction() {
    printf("What?");
}

错误应该是“SomeOtherFunction未声明”,因为您在声明它之前调用了它。解决这个问题的一种方法是将SomeOtherFunction移到SomeFunction之上。另一种方法是先声明函数的签名:

void SomeOtherFunction();

void SomeFunction() {
    SomeOtherFunction();
}

void SomeOtherFunction() {
    printf("What?");
}

这让编译器知道:在代码的某个地方,有一个名为SomeOtherFunction的函数返回void,不接受任何参数。因此,如果您遇到试图调用SomeOtherFunction的代码,不要惊慌,而是去寻找它。

现在,假设在两个不同的.c文件中有SomeFunction和SomeOtherFunction。然后你必须在Some.c中#包含“SomeOther.c”。现在,向other .c中添加一些“私有”函数。由于C不知道私有函数,因此该函数也可以在Some.c中使用。

这就是.h文件的作用:它们指定你想从一个.c文件中“导出”的所有函数(和变量),这些函数可以在其他.c文件中访问。这样,您就获得了类似于Public/Private作用域的东西。此外,您可以将这个.h文件提供给其他人,而不必共享您的源代码- .h文件也适用于编译过的.lib文件。

主要原因是为了方便,为了源代码保护和应用程序各部分之间的解耦。

那是C。c++引入了类和私有/公共修饰符,所以当你仍然可以询问是否需要它们时,c++ AFAIK仍然要求在使用它们之前声明函数。此外,许多c++开发人员也是或曾经是C开发人员,并将他们的概念和习惯转移到c++中——为什么要改变没有破坏的东西呢?

您似乎在询问如何将定义与声明分开,尽管头文件还有其他用途。

答案是c++不“需要”这个。如果将所有内容都标记为内联(对于类定义中定义的成员函数来说,这是自动的),则不需要分离。你可以在头文件中定义一切。

你想分手的原因可能是:

改善构建时间。 在没有定义源的情况下链接到代码。 避免将所有内容标记为“内联”。

如果你更普遍的问题是,“为什么c++和Java不一样?”,那么我不得不问,“为什么你写c++而不是Java?”- p;

但是,更严重的是,c++编译器不能像javac那样直接进入另一个翻译单元,并找出如何使用它的符号。头文件需要向编译器声明在链接时它期望可用的内容。

所以#include是一个直接的文本替换。如果你在头文件中定义所有东西,预处理器最终会对项目中的每个源文件创建一个巨大的复制和粘贴,并将其提供给编译器。c++标准在1998年被批准的事实与此无关,这是c++的编译环境如此紧密地基于C的事实。

转换我的评论来回答你接下来的问题:

编译器如何找到包含代码的.cpp文件

It doesn't, at least not at the time it compiles the code that used the header file. The functions you're linking against don't even need to have been written yet, never mind the compiler knowing what .cpp file they'll be in. Everything the calling code needs to know at compile time is expressed in the function declaration. At link time you will provide a list of .o files, or static or dynamic libraries, and the header in effect is a promise that the definitions of the functions will be in there somewhere.

c++是这样做的,因为C是这样做的,所以真正的问题是为什么C要这样做?维基百科对此做了一些说明。

较新的编译语言(例如 Java、c#)不使用转发 声明;标识符 从源自动识别 文件和读取直接从动态 符号库。这意味着头文件 不需要文件。