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

时间:2012-03-18 06:31:29

标签: c++ macros c-preprocessor

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

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

5 个答案:

答案 0 :(得分:3)

这绝对是可能的,尽管没有包含警卫是不可思议的不良做法。了解#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 ++中,头文件包含类定义很常见。 include guard会阻止#included文件再次粘贴到源文件中,这意味着您的定义只会在编译的代码中出现一次,并且链接器不会混淆。

不要试图找出何时需要使用它们,而不是在不使用时,只需使用包含警戒。大多数时候避免使用宏是一个好主意;这是一种不邪恶的情况,在这里使用它们并不危险。

答案 1 :(得分:1)

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

另外,您可以使用:

#pragma once

如果您不关心可移植性。

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

  • 这不是标准&amp;因此不便携。
  • 它不太直观,并非所有用户都知道它。
  • 与经典和众所周知的Include Guards相比,它没有什么大的优势。

答案 2 :(得分:1)

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

预处理器宏不赞成,是的。但是,标题包括守卫是一个必要的邪恶,因为替代方案是如此糟糕(#pragma曾经只有你的编译器支持它,所以你失去了可移植性)

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

答案 3 :(得分:1)

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

  • 声明可以重复多次,虽然它们显然需要保持一致,并且某些实体不能被声明(例如enum只能被定义;在C ++ 2011中,也可以声明{{1} } S)。
  • 定义不能重复,只有在真正使用定义时才需要。例如,使用指针或类的引用不需要它的定义,只需要它的声明。
因此,编写标题的方法基本上包括尽可能地避免定义,并尽可能使用声明:这些可以在头文件中重复,或者甚至可以多次包含相应的标题。当您需要从基类派生时,主要需要定义:这是无法避免的,这实际上意味着用户在使用任何派生类之前必须包含基类的头。对于直接在类中定义的成员也是如此,但是使用pimpl-idiom可以将成员定义的需求推送到实现文件。

尽管这种方法有一些优点,但它也有一些严重的缺点。主要优点是它可以实现非常彻底的分离和依赖管理。另一方面,过度侵略性分离,例如使用pimpl-idiom来处理所有事情也会对性能产生负面影响。最大的缺点是许多实现细节对于标头的用户是隐式可见的,因为需要首先明确地包括这个依赖的相应标头。至少,编译器强制您正确获得包含文件的顺序。

从可用性和依赖性的角度来看,我认为普遍的共识是标题最好是自包含的,并且使用包含警卫是较小的邪恶。

答案 4 :(得分:0)

非便携式,非标准

#pragma once

为你工作得好吗?就个人而言,我宁愿使用宏来防止重新入侵,但这是你的决定。