为什么要将整个标头内容放在保护令牌中?

发布于 2024-12-21 16:14:46 字数 1461 浏览 1 评论 0原文

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

与风相奔跑 2024-12-28 16:14:46

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

由于类可能不能被定义多次(就像任何定义一样),因此您不能多次包含具有类定义的头文件。

现在假设您在 foo.hpp 中有一些实用程序类 Foo,并且您有两个独立的模块 a.hppb。 hpp 都需要 Foo。您的主程序必须包含 a.hppb.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 in foo.hpp, and you have two, independent modules a.hpp and b.hpp which both require Foo. Your main program must include a.hpp and b.hpp, but now you're attempting to include foo.hpp, and hence the class definition of Foo, twice.

Enter include guards.

无远思近则忧 2024-12-28 16:14:46

因为它允许您多次 #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).

百变从容 2024-12-28 16:14:46

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

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

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).

坐在坟头思考人生 2024-12-28 16:14:46

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

标头 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, ...

You system does not protect against cyclic inclusion. For example:

Header A:

#include "B.h"
#ifndef A_H_INCLUDED
#define A_H_INCLUDED
// ...
#endif // A_H_INCLUDED

Header B:

#include "A.h"
#ifndef B_H_INCLUDED
#define B_H_INCLUDED
// ...
#endif // B_H_INCLUDED

Source file:

#include "A.h" // includes B, which includes A, which includes B, ...
一向肩并 2024-12-28 16:14:46

一个简单的答案就是编译速度。编译器(例如 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文