我应该在C++程序中包含 <xxxx.h> 还是 <cxxxx>?

Should I include <xxxx.h> or <cxxxx> in C++ programs?

提问人:Alok Save 提问时间:12/15/2012 更新时间:6/21/2020 访问量:14051

问:

  • 我应该在 C++ 程序中包含什么,或者?为什么?stdio.hcstdio
  • 为什么两个头文件提供相同的功能?
  • 标准对此有何规定?
  • 我应该如何包含其他此类标头,是否有我应该遵循的基本规则?
C 包括 C++-FAQ

评论


答:

69赞 Alok Save 12/15/2012 #1

请考虑以下程序:

示例 1:

#include<stdio.h>

int main()
{
    printf("Hello World");
    return 0;
}

示例 2:

#include<cstdio>

int main()
{
    printf("Hello World");
    return 0;
}

两者都按预期工作。那么哪种用法更合适呢?答案是:两者都不是!惊讶?请继续阅读。

出于兼容性原因,C++ 标准库提供了所有标准 C 头文件,而 C++ 作为一种语言也提供了所有等效的头文件。按照惯例,

  • 没有 C++ 标准库头文件(除了用于 C 兼容性的头文件)具有任何文件扩展名,并且
  • 所有 C++ 等效的 C 标头都以 开头。cxxxxx

C++ 标准在附录 D(规范)兼容性功能中提到了这一点:

Standard Citation

§2 提到了重要的区分点。适用于上述示例的此规则意味着:

  • 包括 cstdio 导入 std 命名空间中的符号名称,也可能导入 Global 命名空间中的符号名称。
  • 包括 stdio.h 导入 Global 命名空间中的符号名称,也可能导入 std 命名空间中的符号名称。

让我们将此规则应用于示例代码,并衡量优缺点:

示例 1:这会将 stdio.h 中的所有符号引入全局命名空间中。优点是,您可以不加任何限定条件地使用符号,因为它们是在全局命名空间中导入的。缺点是,您最终会用许多可能永远不会使用的符号名称污染全局命名空间。这可能会导致符号名称冲突。在 C++ 中,始终将全局命名空间视为雷区,并尽可能避免它。

示例 2:这是一种非常糟糕的做法,因为不能保证实现会将符号放在全局命名空间中,标准根本不要求这样做。我们只是依赖于一个特定编译器实现的行为。我们不能也不应该假设所有编译器都会这样做。因此,严格来说,该程序未经标准批准,并且这种用法并非在所有实现中都是可移植的。

那么什么是正确的用法呢?

正确的用法是使用并完全限定符号名称,或者使用 using 声明将它们纳入范围。这保证了我们使用的所有符号都存在于命名空间中,并且我们不会污染全局命名空间。正确用法示例:cstdiostd

示例 3:

#include<cstdio>

using std::printf;

int main()
{
    printf("Hello World");
    return 0;
}

请注意,使用 namespace std; 的指令(尤其是在标头中)不是一个好的选择,您应该始终使用声明。using

请注意,我们考虑 vs. 这里只是一个示例用例,实际上它适用于所有 MOST 和标头,除了 <math.h> 和 <cmath> 等少数stdio.hcstdiocxxxxxxxx.h

评论

1赞 Mikael Persson 12/15/2012
只是为了补充这一点。如果可能的话,我总是尝试将 C 头文件的使用限制在 cpp 文件(并将它们包含在其他所有文件之后)。我尽量避免将它们包含在任何 C++ 头文件中。由于 C 头文件没有声明许多类型(主要是函数),因此通常只需要在 cpp 文件中(如果需要的话)它们。
6赞 Cheers and hth. - Alf 12/15/2012
-1 关于“我们没有污染全局命名空间”,您刚刚引用并讨论了情况并非如此。所以。有点自相矛盾。这很重要,因为它直接影响到什么是最佳实践的结论。你上面的结论是错误的。这很奇怪。你首先讨论为什么这是一个糟糕的选择(代码可能无法跨编译器工作,意外的命名空间污染),然后你只是断言这是“正确的用法”。从字面上看,这是正确的,因为它没有错,但这是一个非常糟糕的选择。
0赞 Alok Save 12/15/2012
@Cheersandhth.-Alf:我不明白你想说什么。直接原因可能是英语不是我的第一语言。
1赞 Cheers and hth. - Alf 3/27/2013
@AlokSave:见[stackoverflow.com/a/10694346/464581],包括评注。引用 Steve Jossop 的话:“如果你包括 math.h,那么你就知道你在全局命名空间中丢弃了一堆垃圾。如果包含 cmath,那么可能会也可能不会在全局命名空间中丢弃一堆垃圾。相反,你不在乎你是否在 namepace std 中丢了一堆垃圾,因为你自己从来没有在那里定义过符号。因此,从某种意义上说,math.h 做什么的不确定性比 cmath 做什么的不确定性要好,不管委员会怎么想。
4赞 Ruslan 8/24/2016
所以。。。关于正确用法的结论看起来毫无根据......如果在这两种情况下,全局命名空间都可能受到污染,以至于创建自己的函数无法移植,为什么使用 + 比 + 更好?cstdiostd::printfstdio.h::printf::printf
7赞 Santiago Varela 3/25/2017 #2

由于这篇文章有点旧,我想分享以下内容:


查看代码:

Using X.h   // Compatible with C language standard
---------------
#include <X.h>

int main() {
    // Invoke X's corresponding function
    return 0;
}

Using X    // Not compatible with C language standard
--------------
#include <X>

int main() {
    // Invoke X's corresponding function
    return 0;
}

它们编译和执行都很好!


C++ 哪个更好?

关于 C++11C++17 的规范:

C.5.1(来自 C++17 文档的部分)
对标头的修改 [diff.mods.to.headers]

  1. 为了与 C 标准库兼容,C++ 标准库提供了 D.5 中枚举的 C 标头,但它们的用法是 在 C++ 中已弃用。
  1. C 头文件 、 和 没有 C++ 头文件 ,也没有 C 头文件 本身<stdatomic.h><stdnoreturn.h><threads.h>

C++ 的一部分。

  1. C++ 标头 (D.4.1) 和 (D.4.4) 及其对应的 C 标头和 不<ccomplex><ctgmath><complex.h><tgmath.h>

包含 C 标准库中的任何内容,而不是 仅包含 C++ 标准库中的其他标头。


D.5 C 标准库头文件 [depr.c.headers]

  1. 为了与 C 标准库兼容,C++ 标准库提供了表 141 中所示的 C 头文件。

enter image description here

C++11C++17 标准规范文档都指出了 remains 的使用是为了与 C 标准兼容,尽管它们的使用被视为已弃用<X.h>


关于 C++ 20 标准提案

他们正在审查“不推荐使用”C++20 中的 C 库头文件。 以绿色突出显示。截至目前,C++ 和 C++ 17 的弃用被声明为“弱推荐”,并且保留“C 标准库头文件 (c.headers)”的“调整”如下所示:<X.h>

“基本的 C 库头文件是必不可少的兼容性功能,而且不会很快消失。”(摘自 C++ 20 审查文档)


D.5 C 标准
库头文件 [depr.c.headers]

弱推荐:除上述内容外,还要删除 来自 C++ 标准的相应 C 头文件,就像我们没有一样 对应的 、 或 、 标头。 如上所述,但进行了以下调整: 20.5.5.2.1 C 标准库头文件 [c.headers]<stdatomic.h><stdnoreturn.h><threads.h>

为了与 C 标准库兼容,C++ 标准 库提供了表 141 中所示的 C 头文件。表141 — C 头

 <assert.h>  <inttypes.h>   <signal.h>      <stdio.h>   <wchar.h>
 <complex.h> <iso646.h>     <stdalign.h>    <stdlib.h>  <wctype.h>
 <ctype.h>   <limits.h>     <stdarg.h>      <string.h>  
 <errno.h>   <locale.h>     <stdbool.h>     <tgmath.h>
 <fenv.h>    <math.h>       <stddef.h>      <time.h>
 <float.h>   <setjmp.h>     <stdint.h>      <uchar.h>
 

标头的行为就好像它只包含标头 一样。 标头的行为就像它只包含标头和 .<complex.h><complex><tgmath.h><complex><cmath>


Bjarne Stroustrup 建议最大限度地提高 C 和 C++ 语言,通过减少不兼容性 可能。其他人则不这么认为,因为它使事情复杂化。

所以,它似乎不会去任何地方。最终,您可以同时使用两者。就我个人而言,我会决定使用哪一个,归结为是否让你的代码与 C 代码向后兼容<X.h>

评论

3赞 Pharap 3/25/2017
考虑到当我开始编程时,C++11 仍然被认为是一个新的重大事件,C++17 刚刚出炉并且人们已经在考虑 C++20 的想法非常有趣。
1赞 Santiago Varela 3/25/2017
@Pharap这很可怕,我也有同感。我从 C++03 开始。然后花了 8 年时间才提出一个新标准。现在,我们有了C++17,他们已经在争论C++20了。标准变化更快。很难跟上......
3赞 Adrian McCarthy 3/28/2017
Stroustrup的文章来自~15年前,当时C++的生存与C的流行更紧密地联系在一起。目前尚不清楚这在今天是否同样重要。虽然是否将语言设计得尽可能兼容是一个有趣的问题,但我看不出关于这一点的意见应该如何指导我们应该如何编写 C++ 代码。如果我的代码只作为 C++ 代码进行过测试,那么努力保持它与 C 的兼容性似乎是可疑的,尤其是以全局命名空间污染为代价。
0赞 daparic 9/25/2018
对于嵌入式系统开发,stdio.h 是一个头文件,您可以对其进行 grep 文件系统以证明头文件存在。另一方面,我们如何探测文件系统中是否存在看起来像的头文件?考虑到市面上有无数的编译器变体,这在确定电路板上最基本的东西时听起来很麻烦。cstdio
1赞 Pharap 2/6/2019
@northerner 最初的标准是 C++98,然后 5 年后他们发布了 C++03,其中只包括一些小的修改,主要是对标准的措辞。下一个标准被昵称为“C++0x”,因为它最初预计在 08 年或 09 年左右发布(03 年后的 3-4 年),但由于各种原因它不断被推迟,最终变成了 C++11。(延迟很可能是由于对添加的重大更改数量的争论 - 最初的目标是专注于改进标准库。...