C++:如何在不同命名空间中包含具有相同名称的多个头文件

10

如何解决通过包含一个与另一个头文件同名的头文件而间接包含了另一个头文件的问题?

例如:

// src/blah/a.hpp
#ifndef A_HPP
#define A_HPP

namspace blah
{

class A
{
}

}

#endif

// src/blah/b.hpp
#ifndef B_HPP
#define B_HPP

#includes "a.hpp"

namspace blah
{

class B
{
}

}

#endif

// src/foo/a.hpp
#ifndef A_HPP
#define A_HPP

namspace foo
{

class A
{
}

}

#endif

// src/foo/c.hpp
#ifndef C_HPP
#define C_HPP

#includes "../b.hpp"
#includes "a.hpp"       // won't be included due to multiple inclusion prevention

namspace foo
{

class C
{
}

}

#endif

在上一个头文件中,由于有多重包含预处理器保护,a.hpp不会被包含。实际上,这应该是可以的,因为这些类位于不同的命名空间中。我知道最简单的方法是只需更改foo/a.hpp的名称或在多重包含保护中使用虚假名称。但是否有更好的方法呢?
编辑: 我知道你可以通过在#define和#ifndef指令中使用更具描述性的名称来解决此问题(例如FOO_A_HPP和BLAH_A_HPP),但我想知道这是否是推荐或最佳方法。有些人会推荐使用不同的文件名作为更好的解决方案吗?还是说这并不重要?你会推荐使用哪种约定?
<NAMESPACE>_<CLASS>_HPP

代替

<CLASS>_HPP

为了更好地避免这些问题,有什么建议?

为什么这被标记为C语言? - Oliver Charlesworth
我总是将命名空间放在我的守卫中。自动化工具会将GUID作为头文件守卫。整个重点在于它们必须是唯一的(不仅在您的项目中,而且最好在所有文件和所有维度中都是唯一的)。 - Martin York
5个回答

12

为避免在不同命名空间中使用相同的 #define,建议采用BLAH_A_HPP 和 FOO_A_HPP等名称,这样 #define 中也包含命名空间名称。

编辑:个人建议:

1)不要使用相同的头文件名称(即使用不同的文件名…但这并不总是有用),使用不同的 #define 名称。
2)不要将类命名为相同的名称。每个头文件只放置一个类,并将头文件命名为该类的名称。
3)如果它们通过命名空间进行区分,请在文件名和 #define 中使用该命名空间。
4)将唯一标识符添加到您的 #define 中(例如,我可以使用GOZ)。
5)使用 #pragma once,对于使用它的编译器非常有用。

这完全取决于个人口味。选择适合自己的方案并坚持下去。没有对错之分,只要它可行并保持一致即可。


明白了,我在问题上进行了更多的澄清。你一般推荐使用NAMESPACE_CLASS_HPP的约定吗?这比仅仅更改文件名更好,还是无关紧要? - deuberger
@deuberger:更改文件名不会有任何影响;问题是由于重复的包含保护名称引起的。是的,我建议使用类似NAMESPACE_CLASS_HPP的东西作为包含保护。 - Mike Seymour
是的,这只是一个“部分解决方案”...我会修复它 :) - Goz

4
我很惊讶没有人提到以下使用 #undef A_HPP 的简单解决方案。
//src/foo/c.hpp
#ifndef C_HPP
#define C_HPP

#includes "../b.hpp"
#undef A_HPP
#includes "a.hpp" 
...

这对于只能或被允许更改foo/c.hpp文件的情况特别有用。

1

当MSVC创建一个类时,它会向您的保护名称添加一个GUID以避免这种问题。


这里没有任何东西表明他正在使用MSVC,这也不可能,因为它是C++。 - alternative
@mathepic:你说这不可能,因为它是C++,这是什么胡说八道?给出一个标准的例子来确保头文件保护唯一,并解释这个想法的背景,而不是说必须使用MSVC。 - Greg Domjan
@mathepic:不,它绝对包括C++,但这仍然不是重点。 - Greg Domjan

0

虽然在 C 中没有命名空间,但我认为这对于 CC++ 都适用。

#ifndef BLAH_B_HPP

并且

#ifndef FOO_B_HPP

0

正如您自己建议的那样,完成此操作的常规方法是通过附加GUID、完整路径或其他方式使您的保护标识符唯一。

在VC++中,还有一个pragma可以为您处理多重包含,因此您不需要编写#if defined(HUMPTYDUMPTY)部分,它的格式如下:

#pragma once

现在已经有几个 VC++ 迭代版本,但对于其他编译器,您需要进行检查。这样还可以避免寻找唯一名称的麻烦。

编辑:我刚刚查了一下,互联网认为这也适用于 GCC。看来 #include 样式保护非常符合 20 世纪的风格...


不错的观点,尽管似乎 #pragma 一般和 #pragma once 不是首选方式。请参见:https://dev59.com/aHRA5IYBdhLWcg3w6SNH。 - deuberger
刚刚看了关于编译指示 pragma 的帖子。我并不担心性能问题;只是认为你需要输入的内容越多,出错的可能性就越高。但如果你要在不兼容的编译器之间频繁移植代码,那可能会成为一个问题。无论如何,这都不是让我失眠的问题。 - Alexander Rautenberg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接