为什么要把整个报头内容放在保护令牌中
Why to put the entire header content within guard tokens?
C和c++区分声明和定义。
你可以多次声明一个符号,但是你只能定义它一次。通过学习这个,我有一个想法,把声明放在守卫之外,而定义放在守卫之内:
// declarations
int foo(int x, int y);
int bar(int x, int y);
extern double something;
class X;
#ifndef _MY_HEADER_H_
#define _MY_HEADER_H_
#include "otherheader1.h"
#include "otherheader2.h"
// definitions of structures, classes.
class X
{
// blah blah...
};
#endif
通过这种方式,我可以按照我想要的顺序包含我的标题。也许循环依赖不会成为问题。
那么,如果我们可以把声明放在外面,为什么要用保护令牌保护整个头文件呢?
我的理由如下:
当两个标头以某种方式相互引用时,您可能经常遇到问题。您通常会得到一个未声明的符号错误,您的第一反应是包含必要的标头。但是当你的两个头文件碰巧相互包含时,你会得到一个神秘的错误。
a.h:
#ifndef A_H
#define A_H
#include "b.h"
class A {B *b;}
#endif
b.h
#ifndef B_H
#define B_H
#include "a.h"
class B {A *a;}
#endif
当在b.p cpp中包含b.h时,在a.h中会得到一个错误,即没有声明B,但包含了头文件。(这是一个wtf时刻。)
这是因为头守卫不会嵌套:
#ifndef B_H
#define B_H
#ifndef A_H
#define A_H
// B_H already defined no include here.
class A {B *b;}
#endif
class B {A *a;}
#endif
如果你把声明放在保护之外,你可以防止这种情况发生:
class B; // in b.h
#ifndef B_H
#define B_H
class A; // in a.h
#ifndef A_H
#define A_H
class B; // again from b.h
// B_H already defined no include here, no redefinition.
class A {B *b;}
#endif
class B {A *a;}
#endif
没问题。
更新:将标题包含到守卫中(对不起,这是一个错误)。
当你只考虑"声明"时,你错过了一半的故事。c++还有"类定义"的概念,这是第三种新的动物——它既是定义(类的)又是声明(成员函数的)。
由于类不能被定义不止一次(就像任何定义一样),所以不能在中包含不止一次类定义的头文件。
现在假设您在foo.hpp
中有一些实用程序类Foo
,并且您有两个独立的模块a.hpp
和b.hpp
,它们都需要Foo
。你的主程序必须包括a.hpp
和b.hpp
,但现在你试图包括foo.hpp
,因此Foo
的类定义,两次。
因为它允许您多次#include
头而不用担心冲突。
如果你有一层嵌套,这是不必要的,如果你有几个嵌套,这是必不可少的(考虑包括h1,然后包括h2,其中包括h1,因为它需要它)。
如果是严格意义上的头符保护,则不需要这样做——如果包含multiply,则声明已经可见。
不这样做的另一个原因是,在严格的头文件保护之外的声明可能会禁用编译器对多个包含的头文件的优化(也就是说,它会打开头文件更多次)。
您的系统不能防止循环包含。例如:
标题:
#include "B.h"
#ifndef A_H_INCLUDED
#define A_H_INCLUDED
// ...
#endif // A_H_INCLUDED
头B:
#include "A.h"
#ifndef B_H_INCLUDED
#define B_H_INCLUDED
// ...
#endif // B_H_INCLUDED
源文件:#include "A.h" // includes B, which includes A, which includes B, ...
一个简单的答案就是编译速度。编译器(如GCC和其他编译器)可以检测到完整的文件头保护,并在多次遇到这些文件时避免读取和重新处理这些文件。如果您没有将整个文件包装在头保护中,那么您将很有可能迫使编译器在每次遇到头时重新计算头。
- 如何在C++中使用X509证书模在令牌中查找私钥
- holeMenuProgram.cpp:38:1 错误:'}'令牌之前的预期主表达式
- C++:"("令牌"之前有预期的非限定 id 指向类中成员函数的指针
- Arduino 用于语句错误。令牌之前的预期')' ';'。如何解决这个问题?
- 错误:令牌 { '{' 之前应存在非限定 ID
- 允许哪些令牌作为 #include 的参数?
- "错误 C0000:语法错误,令牌"<EOF>"处出现意外$end,并且不确定
- 它在 { 令牌之前给了我预期的标识符
- (错误:令牌之前'<<'预期的主表达式)
- 为什么 ## aka 令牌粘贴运算符不适用于 C 和 C++ 中的注释?
- 错误:"->"令牌之前的预期初始值设定项
- LINUX 操作系统上的错误:令牌之前预期的构造函数、析构函数或类型转换'('?
- 丢失读入的每一行输入中的最后一个令牌
- 如何使用 libCurl 将访问令牌发送到服务器 API
- C++令牌定义成员
- 宏定义中的预处理器令牌两边有两个双引号
- 错误:C++中']'令牌之前的预期主表达式
- 错误:令牌之前的预期主表达式'&&'
- 令牌之前']'预期的主表达式?
- 为什么要把整个报头内容放在保护令牌中