是否可以在没有包含保护且没有多个定义错误的情况下编写头文件?

Posted

技术标签:

【中文标题】是否可以在没有包含保护且没有多个定义错误的情况下编写头文件?【英文标题】:Is it possible to write header file without include guard, and without multiple definition errors? 【发布时间】:2012-03-18 06:31:28 【问题描述】:

出于好奇,我想知道是否有办法实现这一目标。

在 C++ 中,我们知道应该避免使用宏。但是当我们使用包含守卫时,我们确实使用了至少一个宏。所以我想知道是否有办法编写无宏程序。

【问题讨论】:

“应避免使用”与“不得使用”的含义截然不同。使用头卫。如果您的编译器支持,您可以使用#pragma once,但也可以使用包含保护(如果编译器不理解,编译指示将被忽略)。 【参考方案1】:

这绝对是可能的,尽管不包含警卫是难以想象的糟糕做法。了解#include 语句的实际作用很重要:另一个文件的内容在编译之前直接粘贴到您的源文件中。包含保护可防止再次粘贴相同的代码。

仅当在您包含该文件的位置键入该文件的内容不正确时,包含该文件才会导致错误。例如,您可以在单个编译单元中多次声明(注意:声明,而不是定义)同一个函数(或类)。如果您的头文件仅包含声明,则无需指定包含保护。

IncludedFile.h

class SomeClassSomewhere;
void SomeExternalFunction(int x, char y);

Main.cpp

#include "IncludedFile.h"
#include "IncludedFile.h"
#include "IncludedFile.h"

int main(int argc, char **argv)

    return 0;

虽然多次声明一个函数(或类)是可以的,但多次定义同一个函数(或类)是不行的。如果一个函数有两个或多个定义,则链接器不知道该选择哪一个并放弃“多重定义符号”错误。

在 C++ 中,头文件包含类定义是很常见的。包含保护可防止 #included 文件再次粘贴到源文件中,这意味着您的定义只会在编译的代码中出现一次,并且不会混淆链接器。

与其试图弄清楚什么时候需要使用它们,什么时候不需要,只需要始终使用包含保护。大多数时候避免使用宏是个好主意;这是一种它们不是邪恶的情况,在这里使用它们并不危险。

【讨论】:

我在 *** 上到处寻找这个答案!【参考方案2】:

这绝对是可行的,我使用了一些早期的 C++ 库,这些库遵循了 C 中已经被误导的方法,基本上要求头文件的用户在此之前包含某些其他头文件。这是基于彻底理解是什么造成了对其他内容的依赖,并尽可能使用声明而不是定义:

声明可以重复多次,尽管它们显然需要保持一致并且某些实体不能声明(例如,enum 只能定义;在 C++ 2011 中,还可以声明 enums)。 定义不能重复,只有在真正使用定义时才需要。例如,使用指针或类的引用不需要定义,只需要声明。

因此,编写头文件的方法本质上是尽量避免定义,并尽可能只使用声明:这些可以在头文件中重复,或者相应的头文件甚至可以包含多次。当您需要从基类派生时,首先需要定义:这是无法避免的,本质上意味着用户必须在使用任何派生类之前包含基类的标头。直接在类中定义的成员也是如此,但使用 pimpl-idiom 可以将成员定义的需求推送到实现文件中。

虽然这种方法有一些优点,但它也有一些严重的缺点。主要优点是它强制执行非常彻底的分离和依赖管理。另一方面,过度激进的分离,例如对所有内容都使用 pimpl-idiom 也会对性能产生负面影响。最大的缺点是很多实现细节对标头的用户是隐式可见的,因为这个依赖的各个标头需要首先显式包含。至少,编译器会强制您正确获取包含文件的顺序。

从可用性和依赖性的角度来看,我认为人们普遍认为标头最好是自包含的,而使用包含守卫是较小的弊端。

【讨论】:

【参考方案3】:

如果您确保同一个头文件没有多次包含在同一个翻译单元中,则可以这样做。

另外,你可以使用:

#pragma once

如果您不关心可移植性。

但是,您应该避免使用 #pragma once 而不是 Include Guards,因为:

它不是标准的,因此不可移植。 它不太直观,并非所有用户都知道。 与经典且广为人知的 Include Guards 相比,它没有太大优势。

【讨论】:

#pragma once 有一个小优势:编译器会读取一次文件。您可以将它与传统的头部防护一起使用(无论如何您都应该使用头部防护)。 @AlexandreC。传统的标头保护很容易检测,并且跳过预处理(需要标记化)非常有利,任何体面的编译器都会实现优化并以任何方式执行相同的操作。 @Potatoswatter:无论如何,如果有的话,差异应该很小。【参考方案4】:

简而言之,是的,即使没有编译指示。仅当您可以保证每个头文件只包含一次时。然而,考虑到代码的增长趋势,随着头文件数量的增加,兑现这一保证变得越来越困难。这就是为什么不使用标头保护被认为是不好的做法。

预处理器宏不受欢迎,是的。但是,标头包含保护是必要的,因为替代方案要糟糕得多(#pragma once 仅在您的编译器支持时才有效,因此您失去了可移植性)

关于预处理器宏,请使用以下规则: 如果您能想出一个不涉及宏的优雅解决方案,请避免使用它们。

【讨论】:

【参考方案5】:

非便携、非标准

#pragma once

对你来说足够好?就个人而言,我宁愿使用宏来防止重新包含,但这是您的决定。

【讨论】:

以上是关于是否可以在没有包含保护且没有多个定义错误的情况下编写头文件?的主要内容,如果未能解决你的问题,请参考以下文章

是否可以在没有 php 的情况下编写 Wordpress 主题? [关闭]

犯错误:多个定义...尽管包括警卫

如何在没有错误消息的情况下调试 ctypes

在没有 sse 的情况下编译 OpenCV 以在 ROS 中使用

如何在没有 Ivy 的情况下编译库?

如何使用 CL 在没有主函数的情况下编译 C?