C++ headers 区别
C++ headers difference
刚接触编程,对 headers 有一些疑问。
书中用到的:
#include "std_lib_facilities.h"
我的老师总是使用:
#include <iostream>
using namespace std;
为什么我们会使用一个而不是另一个?一个工作比另一个好?任何帮助,将不胜感激。
随书提供或在线访问的 include_std_facilities.h 文件就像是包含多个头文件的快捷方式(如果包含该文件,查看源代码,它只包含一些有用的头文件,例如您不需要自己明确包含它们)。
可以把它想象成邀请你妈妈来吃晚饭。你邀请她,这不可避免地意味着你的父亲(好吧,我在做假设)、你的妹妹和她那条讨厌的活泼的狗也会自动过来吃晚饭,而不会明确要求他们。
这不是一个很好的实践,但我认为作者有他们的理由。
你的老师是对的,最小代码通常会包含iostream,以便你可以输出到终端。但是,这是我必须强调的一点,使用 namespace std 是不好的做法。您可以通过显式使用 std:: 访问 std 功能 - 例如:
std::cout << "hello world!" << std::endl;
出于多种原因,在 std 命名空间之外工作是有益的。您可以通过一些谷歌搜索找到这些。
我猜这本书的作者这样做是为了让代码示例更短,而不是作为示例风格的示例。无需在每个示例中都放入样板文本。
就我而言,将一堆包含文件放入每个人都包含的头文件中(这不是预编译的头文件)会大大减慢编译速度(不仅需要查找和加载每个文件,还需要检查依赖项等),特别是如果您有一个过分热心的 IT 部门坚持实时扫描每个文件访问。
文件中包含的 header 根据文件的需要从文件更改到文件。 Stroustrup 使用的内容在他的源代码示例的上下文中起作用。您的老师使用的内容符合老师的需求。
你使用什么取决于你的需要和程序的需要。
每个文件应包含 所有 的 header 项,仅此而已。如果在文件中使用 std::string
,则 #include <string>
。如果使用std::set
、#include <set>
。
似乎没有必要包含某些 header 文件,因为另一个包含包含它们。例如,既然 iostream 已经包含了字符串,为什么还要同时包含 iostream 和字符串呢?实际上这可能无关紧要,包含了字符串,但它现在是一个维护问题。并非给定 header 的所有实现都具有相同的包含。您会发现自己遇到一些有趣的情况,其中代码在一个编译器或编译器版本中编译,而不是另一个编译器,因为包含依赖项在 header 可能有两个或三个包含链中发生变化。
最好为您和所有可能跟随您维护代码的人避免这种情况。
如果您不关心维护,请查看 y2k 错误。这是一个问题,因为为满足 1960 年代和 1970 年代的需求和限制而编写的软件在 40 年后仍在需求和限制截然不同的世界中使用。您会惊讶于有多少代码超过了预期的生命周期。
虽然在你的余生中有一份固定的工作是件好事,但在夏威夷度假的周末却很糟糕,因为 co-op 本来可以完成的修改变成了一场噩梦,因为 GCC 7.12 与您最初编写程序时所用的 GCC 5.2 完全不同。
您还会发现,随着包含长文件链,构造不当的 header 可能会在 header 上注入顺序依赖性。如果 header Y 包含在 header Z 之前,程序 X 工作正常。这是因为 Z 需要 Y 包含的 Z 没有声明的东西。不要成为写Z的人。
但是,不要包含所有内容。您包含的内容越多,编译所需的时间就越长,并且您对无法预料的组合反应不良的风险就越大。不管喜欢与否,总是有人写 header Z,如果不包含 Z,您不希望调试 Z 导致的日程安排受到影响。
为什么编译时间变长了?将每个包含视为编译器(实际上是预处理器)将包含文件粘贴到包含文件中的命令。然后编译器编译组合文件。每个包含都需要从磁盘加载(慢),可能需要进行安全扫描(通常非常慢),然后合并到要编译的文件中(比您预期的要快)。所有被包含文件的包含都被粘贴进去,在你知道之前,你有一个巨大的文件母亲要被编译器解析。如果不需要那个巨大文件的很大一部分,那就是浪费精力。
避免像需要 header Y including header Z 和 header Z including Y 的瘟疫结构。一个 include 守卫,稍后会详细介绍,将保护明显的递归问题(Y includes Z includes Y includes Z includes...),但也确保您不能及时包含 Y 或 Z 以满足 Z 或 Y。
一些常见的headers,字符串是最喜欢的,一遍又一遍地包含在内。您不想重新包含 header 及其依赖项,但您还想确保它已包含(如果未包含)。为了解决这个问题,你用 Header 守卫包围你的 header。这看起来像:
#ifndef UNIQUE_NAME
#define UNIQUE_NAME
// header contents goes here
#endif
如果UNIQUE_NAME
没有定义,则定义它,并复制要编译的文件中的header内容。这有几个问题。如果 UNIQUE_NAME
不是唯一的,您将收到一些非常奇怪的错误 "not found" 消息,因为第一个 header 守卫将阻止下一个
的包含。
唯一性问题已通过 #pragma once
解决,但 #pragma once
自身存在一些问题。它不是标准的 C++,因此并未在所有编译器中实现。 #pragma
如果编译器不支持 pragma,指令将被静默忽略(除非编译器警告被调高,有时甚至不会)。随着 headers 被重复包含在内,随之而来的是彻底的混乱,你会收到警告。 #pragma once
也可能被包括网络地图和链接在内的复杂目录结构所愚弄。
所以...
Stroustrup 使用 #include "std_lib_facilities.h"
是因为它包含了他书中早期课程所需的所有细节,而不必冒着覆盖这些细节 nitty-gritty 的信息过载的风险。这是经典的鸡肉和鸡蛋。 Stroustrup 希望通过减少学生的信息负荷以可控的方式教授这些早期课程,直到 Stroustrup 能够涵盖实际理解背景所需的 material这些早期课程的基本细节。
很棒的教学策略。在某种程度上,该策略值得在代码中效仿:他通过添加一个间接层消除了一个问题。
但在程序员的滑动发布日期、pointy-haired 老板、死亡行军和诉讼的现实世界中,这可能是一个糟糕的策略。您不想浪费时间来解决一开始不需要存在的非问题。不存在的代码没有错误。或者是一个错误,但那是另一个问题。
Bjarne Stroustrup 可以逃脱一些事情,因为他 Bjarne 咒骂删除了 Stroustrup。他知道自己在做什么。他证明了这一点。他可能已经考虑、衡量和权衡了他的 megaheader 的含义,因为他知道它们是什么。如果他担心里面有什么东西会影响他的学生,他就不会这样做了。
一年级编程学生不是 Bjarne Stroustrup。即使他们有一颗行星那么大的大脑,他们也缺乏多年的经验。它通常是您不知道的东西,您需要担心,而 Stroustrup 的 "don't know" 池将明显变小。
当您知道自己在做什么并且可以诚实地证明所做的事情合理时,您可以做各种不安全的事情。并且只要您考虑到您可能不是唯一对代码有既得利益的人。如果在您被公共汽车撞到后拿起您的投资组合的编码员无法阅读它,那么您的 super-elegant-but-super-arcane 解决方案并不多。您可爱的代码将进入垃圾箱,您的遗产将是,"What the smurf was that smurfing idiot trying to do with that smurf?"至少留下一些笔记。
Stroustrup 也有页数和字数限制,要求他在印刷版中压缩文本。一个 Header 来统治他们的一切帮助极大。
您的老师在学生超载方面也有类似的问题,但可能没有打印 space 约束。对于您目前看到的代码片段,所需要的只是需要 iostream 的基本输入和输出。可能还有字符串,但即使我承认如果不包含字符串也很难编写 iostream header。
很快您将开始看到 #include <vector>
、#include <fstream>
和 #include<random>
,并开始编写您自己的 header。
努力学会正确地做事,否则毕业后你的软件工程学习曲线很糟糕。
刚接触编程,对 headers 有一些疑问。
书中用到的:
#include "std_lib_facilities.h"
我的老师总是使用:
#include <iostream>
using namespace std;
为什么我们会使用一个而不是另一个?一个工作比另一个好?任何帮助,将不胜感激。
随书提供或在线访问的 include_std_facilities.h 文件就像是包含多个头文件的快捷方式(如果包含该文件,查看源代码,它只包含一些有用的头文件,例如您不需要自己明确包含它们)。
可以把它想象成邀请你妈妈来吃晚饭。你邀请她,这不可避免地意味着你的父亲(好吧,我在做假设)、你的妹妹和她那条讨厌的活泼的狗也会自动过来吃晚饭,而不会明确要求他们。
这不是一个很好的实践,但我认为作者有他们的理由。
你的老师是对的,最小代码通常会包含iostream,以便你可以输出到终端。但是,这是我必须强调的一点,使用 namespace std 是不好的做法。您可以通过显式使用 std:: 访问 std 功能 - 例如:
std::cout << "hello world!" << std::endl;
出于多种原因,在 std 命名空间之外工作是有益的。您可以通过一些谷歌搜索找到这些。
我猜这本书的作者这样做是为了让代码示例更短,而不是作为示例风格的示例。无需在每个示例中都放入样板文本。
就我而言,将一堆包含文件放入每个人都包含的头文件中(这不是预编译的头文件)会大大减慢编译速度(不仅需要查找和加载每个文件,还需要检查依赖项等),特别是如果您有一个过分热心的 IT 部门坚持实时扫描每个文件访问。
文件中包含的 header 根据文件的需要从文件更改到文件。 Stroustrup 使用的内容在他的源代码示例的上下文中起作用。您的老师使用的内容符合老师的需求。
你使用什么取决于你的需要和程序的需要。
每个文件应包含 所有 的 header 项,仅此而已。如果在文件中使用 std::string
,则 #include <string>
。如果使用std::set
、#include <set>
。
似乎没有必要包含某些 header 文件,因为另一个包含包含它们。例如,既然 iostream 已经包含了字符串,为什么还要同时包含 iostream 和字符串呢?实际上这可能无关紧要,包含了字符串,但它现在是一个维护问题。并非给定 header 的所有实现都具有相同的包含。您会发现自己遇到一些有趣的情况,其中代码在一个编译器或编译器版本中编译,而不是另一个编译器,因为包含依赖项在 header 可能有两个或三个包含链中发生变化。
最好为您和所有可能跟随您维护代码的人避免这种情况。
如果您不关心维护,请查看 y2k 错误。这是一个问题,因为为满足 1960 年代和 1970 年代的需求和限制而编写的软件在 40 年后仍在需求和限制截然不同的世界中使用。您会惊讶于有多少代码超过了预期的生命周期。
虽然在你的余生中有一份固定的工作是件好事,但在夏威夷度假的周末却很糟糕,因为 co-op 本来可以完成的修改变成了一场噩梦,因为 GCC 7.12 与您最初编写程序时所用的 GCC 5.2 完全不同。
您还会发现,随着包含长文件链,构造不当的 header 可能会在 header 上注入顺序依赖性。如果 header Y 包含在 header Z 之前,程序 X 工作正常。这是因为 Z 需要 Y 包含的 Z 没有声明的东西。不要成为写Z的人。
但是,不要包含所有内容。您包含的内容越多,编译所需的时间就越长,并且您对无法预料的组合反应不良的风险就越大。不管喜欢与否,总是有人写 header Z,如果不包含 Z,您不希望调试 Z 导致的日程安排受到影响。
为什么编译时间变长了?将每个包含视为编译器(实际上是预处理器)将包含文件粘贴到包含文件中的命令。然后编译器编译组合文件。每个包含都需要从磁盘加载(慢),可能需要进行安全扫描(通常非常慢),然后合并到要编译的文件中(比您预期的要快)。所有被包含文件的包含都被粘贴进去,在你知道之前,你有一个巨大的文件母亲要被编译器解析。如果不需要那个巨大文件的很大一部分,那就是浪费精力。
避免像需要 header Y including header Z 和 header Z including Y 的瘟疫结构。一个 include 守卫,稍后会详细介绍,将保护明显的递归问题(Y includes Z includes Y includes Z includes...),但也确保您不能及时包含 Y 或 Z 以满足 Z 或 Y。
一些常见的headers,字符串是最喜欢的,一遍又一遍地包含在内。您不想重新包含 header 及其依赖项,但您还想确保它已包含(如果未包含)。为了解决这个问题,你用 Header 守卫包围你的 header。这看起来像:
#ifndef UNIQUE_NAME
#define UNIQUE_NAME
// header contents goes here
#endif
如果UNIQUE_NAME
没有定义,则定义它,并复制要编译的文件中的header内容。这有几个问题。如果 UNIQUE_NAME
不是唯一的,您将收到一些非常奇怪的错误 "not found" 消息,因为第一个 header 守卫将阻止下一个
唯一性问题已通过 #pragma once
解决,但 #pragma once
自身存在一些问题。它不是标准的 C++,因此并未在所有编译器中实现。 #pragma
如果编译器不支持 pragma,指令将被静默忽略(除非编译器警告被调高,有时甚至不会)。随着 headers 被重复包含在内,随之而来的是彻底的混乱,你会收到警告。 #pragma once
也可能被包括网络地图和链接在内的复杂目录结构所愚弄。
所以...
Stroustrup 使用 #include "std_lib_facilities.h"
是因为它包含了他书中早期课程所需的所有细节,而不必冒着覆盖这些细节 nitty-gritty 的信息过载的风险。这是经典的鸡肉和鸡蛋。 Stroustrup 希望通过减少学生的信息负荷以可控的方式教授这些早期课程,直到 Stroustrup 能够涵盖实际理解背景所需的 material这些早期课程的基本细节。
很棒的教学策略。在某种程度上,该策略值得在代码中效仿:他通过添加一个间接层消除了一个问题。
但在程序员的滑动发布日期、pointy-haired 老板、死亡行军和诉讼的现实世界中,这可能是一个糟糕的策略。您不想浪费时间来解决一开始不需要存在的非问题。不存在的代码没有错误。或者是一个错误,但那是另一个问题。
Bjarne Stroustrup 可以逃脱一些事情,因为他 Bjarne 咒骂删除了 Stroustrup。他知道自己在做什么。他证明了这一点。他可能已经考虑、衡量和权衡了他的 megaheader 的含义,因为他知道它们是什么。如果他担心里面有什么东西会影响他的学生,他就不会这样做了。
一年级编程学生不是 Bjarne Stroustrup。即使他们有一颗行星那么大的大脑,他们也缺乏多年的经验。它通常是您不知道的东西,您需要担心,而 Stroustrup 的 "don't know" 池将明显变小。
当您知道自己在做什么并且可以诚实地证明所做的事情合理时,您可以做各种不安全的事情。并且只要您考虑到您可能不是唯一对代码有既得利益的人。如果在您被公共汽车撞到后拿起您的投资组合的编码员无法阅读它,那么您的 super-elegant-but-super-arcane 解决方案并不多。您可爱的代码将进入垃圾箱,您的遗产将是,"What the smurf was that smurfing idiot trying to do with that smurf?"至少留下一些笔记。
Stroustrup 也有页数和字数限制,要求他在印刷版中压缩文本。一个 Header 来统治他们的一切帮助极大。
您的老师在学生超载方面也有类似的问题,但可能没有打印 space 约束。对于您目前看到的代码片段,所需要的只是需要 iostream 的基本输入和输出。可能还有字符串,但即使我承认如果不包含字符串也很难编写 iostream header。
很快您将开始看到 #include <vector>
、#include <fstream>
和 #include<random>
,并开始编写您自己的 header。
努力学会正确地做事,否则毕业后你的软件工程学习曲线很糟糕。