3

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

通过这种方式,我可以包含我想要的任何顺序的标题。并且可能循环依赖不会成为问题。

那么,如果我们可以将声明放在外面,为什么还要用保护令牌来保护整个标头呢?

我的理由如下:

当两个标题以某种方式相互引用时,您可能经常遇到问题。您通常会收到未声明的符号错误,您的第一反应是包含必要的标头。但是,当您的两个标头碰巧相互包含时,您会得到一些神秘的错误。

啊:

#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 未声明但包含标头。(这是一个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

这里没问题。

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

4

5 回答 5

4

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

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

现在假设您有一些实用程序类Foofoo.hpp并且您有两个独立的模块a.hppb.hpp它们都需要Foo. 您的主程序必须包含a.hppand b.hpp,但现在您尝试包含foo.hpp, 并因此包含 , 的类定义Foo两次。

输入包括警卫。

于 2011-12-16T12:09:15.047 回答
2

因为它可以让你#include多次标头而不怕冲突。

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

于 2011-12-16T12:00:03.537 回答
1

如果它是严格的标头保护,则不需要 - 如果包含多个,则声明已经可见。

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

于 2011-12-16T11:59:37.513 回答
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, ...
于 2011-12-16T12:08:03.643 回答
1

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

于 2011-12-16T12:30:39.190 回答