为什么要将整个标题内容放在守卫令牌中?

时间:2011-12-16 11:56:25

标签: c++ c header header-files include-guards

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.cpp中包含b.h时,你会在a.h中得到一个错误,即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

这里没问题。

更新:将标题包含在警卫中(对不起,这是一个错误)。

5 个答案:

答案 0 :(得分:4)

当你只考虑“声明”时,你错过了一半的故事。 C ++也有一个“类定义”的概念,它是第三种新型动物 - 它既是定义(类),也是声明成员函数)。

由于类可以多次定义(就像任何定义一样),一定不能包含一个带有类定义的头文件多次。

现在假设您在Foo中有一些实用工具类foo.hpp,并且您有两个独立模块a.hppb.hpp,它们都需要Foo。您的主要计划必须包含a.hppb.hpp,但现在您尝试将foo.hpp包含在Foo的类定义中,两次。

输入包含警卫。

答案 1 :(得分:2)

因为它允许您多次#include标题,而不必担心冲突。

虽然如果你有一个级别的嵌套是没有必要的,但如果你有几个嵌套是必不可少的(考虑包括h1,然后包括h2,其中包括h1,因为它需要它)。

答案 2 :(得分:1)

没有必要,如果它严格地是一个标题保护 - 如果多次包含,声明已经可见。

另一个原因是严格的标头保护之外的声明可能会禁用多重包含标头的编译器优化(也就是说,它会打开标题更多次)。

答案 3 :(得分:1)

您的系统无法防止循环包含。例如:

标题A:

#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, ...

答案 4 :(得分:1)

一个简单的答案就是编译速度。编译器(如GCC和其他编译器)可以检测到完整的文件头保护,并避免在遇到多次时读取和重新处理这些文件。如果你没有将整个文件包装在标题保护中,那么你很可能会强迫你的编译器在每次遇到它时重新评估标题。