为什么要将整个标头内容放在保护令牌中?
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
通过这种方式,我可以按照我想要的顺序包含我的标头。循环依赖可能不会成为问题。
那么,如果我们可以将声明放在外面,为什么还要用保护令牌来保护整个标头呢?
我的理由如下:
当两个标头以某种方式相互引用时,您可能经常会遇到问题。您通常会遇到未声明的符号错误,您的第一反应是包含必要的标头。但是,当两个标头碰巧相互包含时,您会收到神秘的错误。
ah:
#ifndef A_H
#define A_H
#include "b.h"
class A {B *b;}
#endif
bh
#ifndef B_H
#define B_H
#include "a.h"
class B {A *a;}
#endif
当在 b.cpp 中包含 bh 时,在 ah 中会出现错误,指出 B 未声明,但包含标头。 (这是一个糟糕的时刻。)
这是因为标头防护不会嵌套:
#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 and C++ distinguishes between declarations an definitions.
You can declare a symbol many times, but you are allowed to define it only once. By learning this I have an idea to put declarations outside the guards, while definitions inside the guards:
// 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
By this way I can include my headers whatever order I want. And probably circular dependencies won't be a problem.
So why to protect the entire header with guard tokens if we can put the declarations outside?
My rationale was the following:
You probably often run into issues when two headers refer to each other somehow. You usually get an undeclared symbol error, and your first reflex is to include the necessary headers. But when two of your headers happen to include each other, you get cryptic errors.
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
When in b.cpp you include the b.h you get an error in a.h that B is not declared but the header is included. (It's a wtf moment.)
It's because that header guards won't nest:
#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
If you put declarations outside the guard, you can prevent this:
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
No problem here.
UPDATE: put header inclusion into the guards (Sorry it was a mistake).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
当你只考虑“声明”时,你就错过了一半的故事。 C++ 还有一个“类定义”的概念,这是第三种新的动物类型——它既是(类的)定义,又是(类的)声明成员函数)。
由于类可能不能被定义多次(就像任何定义一样),因此您不能多次包含具有类定义的头文件。
现在假设您在
foo.hpp
中有一些实用程序类Foo
,并且您有两个独立的模块a.hpp
和b。 hpp
都需要Foo
。您的主程序必须包含a.hpp
和b.hpp
,但现在您尝试包含foo.hpp
,因此该类Foo
的定义,两次。输入包括警卫。
You're missing half the story when you think only about "declarations". C++ also has a notion of "class definitions", which are a third, new type of animal -- it's both a definition (of the class) and a declaration (of the member functions).
Since classes may not be defined more than once (just like for any definition), you must not include a header file with class definitions more than once.
Now imagine you have some utility class
Foo
infoo.hpp
, and you have two, independent modulesa.hpp
andb.hpp
which both requireFoo
. Your main program must includea.hpp
andb.hpp
, but now you're attempting to includefoo.hpp
, and hence the class definition ofFoo
, twice.Enter include guards.
因为它允许您多次
#include
标头而不必担心冲突。虽然如果您有一层嵌套,则没有必要,但如果您有多层嵌套,则这是必不可少的(考虑包括 h1,然后包括 h2,其中 h2 包括 h1,因为它需要它)。
Because it allows you to
#include
a header several times without being afraid of conflicts.While it is not necessary if you have one level of nesting, it is indispensable if you have several (consider including h1, then including h2 which includes h1 because it needs it).
如果它是严格的标头保护,则不需要 - 如果包含多重声明,则声明已经可见。
反对这一点的另一个原因是,严格标头保护之外的声明可能会禁用多重包含标头的编译器优化(也就是说,它将多次打开标头)。
there is no need if it is strictly a header guard - the declaration is already visible if multiply included.
another reason against this is that declarations outside a strict header guard may disable compiler optimizations for multiply included headers (that is, it will open the header more times).
您的系统无法防止循环包含。例如:
标头 A:
标头 B:
源文件:
You system does not protect against cyclic inclusion. For example:
Header A:
Header B:
Source file:
一个简单的答案就是编译速度。编译器(例如 GCC 和其他编译器)可以检测完整文件标头保护,并避免在多次遇到时读取和重新处理这些文件。如果您不将整个文件包装在标头保护中,那么您很有可能会强制编译器在每次遇到标头时重新评估标头。
A simple answer would simply be compilation speed. Compilers, like GCC and probably others, can detect a full-file header guard and avoid reading and reprocessing those files when encountered multiple times. If you don't wrap your entire file in the header guards there's a very good chance you'll be forcing your compiler to reevaluate the header every time it is encountered.