2011-08-05 39 views
18

我发现我认为在C++中使用命名空间的“最佳实践”会伤害我的代码的可读性,并使我怀疑如何很好地使用它们。C++命名空间最佳实践困境

我的程序由几个不同的模块组成,这些模块大多建立在“主”应用程序使用的库中。每个库都使用它自己的命名空间,它们的命名空间都位于项目命名空间的“内部”,以帮助避免与第三方代码的名称冲突。所以我最终得到了诸如“myproject :: logging :: Logger”和“myproject :: reporting :: ReportType”之类的类名(就像构成示例一样)。

到目前为止这么好。而在我的.cpp文件中,我没有问题。例如,我在顶部使用“使用myproject :: logging”,并可以干净地引用我的Logging类。在两个命名空间之间发生冲突的情况下,我可以明确地说出我想要哪一个。这很好。

头文件虽然不同。在头文件中使用语句被认为是不好的做法,因为它们会影响不相关的代码,而这些代码可能并不期望它们。所以我总是完全限定.hpp文件中的所有名称。这有点难看,但现在可以管理,所以我忍受了。但现在我正在增加在我的库中使用模板代码,这意味着现在我的.hpp文件中有更多实际代码。由于类型名称的长度,必须完全限定每个名称才能使代码几乎不可读。

我开始觉得命名空间的好处和使用它们的最佳实践开始被我不得不写的代码的不可读性所压倒。我开始怀疑,如果我放弃使用命名空间来获取更多可读代码的好处,并且在出现时修复任何名称冲突,是否会更好。

另一种方法是使用简短的单层命名空间,而不是使用“myproject :: logging :: Logger”我只需要“log :: Logger”,这将有很大帮助,但使命名空间冲突的可能性高得多,并且还有名称空间传达较少有用的信息。

正如我已经说过了,这是我高兴地用我的实现文件“使用命名空间”,以方便管理才真正影响.HPP文件中的代码,但它成为一个问题,因为我看我在.hpp文件中的模板代码现在并认为“eww ....”,这不可能是好的:P

任何人都有任何实际的建议?

+2

'using'指令的作用域,所以也许你可以做一些简写命名空间不头文件外泄漏? –

回答

13

我一直在这种情况下。通常情况下,头文件中的很多模板函数/类实际上都是“实现”,尽管通过C++模板的性质,您不得不将它们放在头文件中。因此,我只是将所有内容放在一些“详细”或“实现”命名空间中,我可以轻松地使用“使用命名空间”。最后,我“放弃”人们应该使用什么到相应的地方。像这样:

namespace myproject { namespace somemodule { 

namespace _implementation { 

using namespace myproject::othermodule; 
using namespace myproject::yetanothermodule; 

template <...> 
class some_internal_metafunction{ 
... 
}; 

template <...> 
class stuff_people_should_use_outside { 
... 
}; 

} // namespace implementation  

using stuff_people_should_use_outside ; 
}} // namespace myproject::somemodule 

虽然这种方法可能会在您的编译器报告中放大一点名称。

或者,您可以放弃模块名称空间。但对于一个非常大的项目来说这可能不是个好主意。

+2

它需要'使用_implementation :: stuff_people_should_use_outside;'? – Hugues

+0

@dsign:你应该考虑更新你的答案! – abhiarora

5

个人?我会摆脱“myproject”部分。您的图书馆将使用与另一个具有完全相同的名称空间名称的机会是什么?

此外,我会建议您希望在标题中使用名称空间的较短的名称。

+6

“对于给定的例子”logging :: Logger“,你的库有可能使用完全相同的命名空间名称,并且使用相同名称定义另一个符号”,我想说这个机会是相当的这个世界上有两个这样的世界。任何试图在一个程序中使用这两个库的人都不太可能,但根据概率,“:: myproject :: Logger”比':: logging :: Logger'更安全(假定“myproject”被替换为项目的实际名称,ofc)。 –

0

如果你的项目不是非常非常巨大(我的意思是非常巨大),只使用myproject应该足够。如果你真的想把你的项目分成几部分,你可以使用更通用的命名空间。例如,如果我正在构建一个游戏引擎,我会去寻找MyEngine :: Core,MyEngine :: Renderer,MyEngine :: Input,MyEngine :: Sound等命名空间。

+0

这不仅仅是项目的规模,还包括代码的不稳定性,发布周期的紧缩程度,类似功能需求/实体的可能性等,以及开发人员之间的实际协调程度。例如,基本上独立的开发人员(在不同的国家/地区)由于发布周期短,编写大量新代码来执行类似的事情(例如,证券的不同定价公式)更可能需要独立的名称空间,而不是单个开发人员主要调整现有功能...... –

2

我的经验是,它很多由于您在原始文章中提到的原因,为所有代码提供一个命名空间更方便。该名称空间可保护您的标识符免受来自第三方库的标识符的冲击。你的名字空间是你的统治地位,很容易保持名字无冲突。

+0

“你的名字空间是你的统治地位” - 如果你用一个GUID或其他东西命名它。独特,令人难忘和简短:挑选两个;-) –

+1

难忘的是没有必要的,你可以随时查找它。所以,独特和短。 –

+1

所以你最终调用你的顶级命名空间'hFPu9_'。实际上,这里没有太多问题,但原则上两个不相关的项目可以选择相同的顶级命名空间,然后遇到问题。这就是Java使用反向域名的原因。它以简洁为代价提供独特性。 –

18

这就是我所做的。

<mylibrary.h>

namespace myproject { 
    namespace mylibrary 
    { 
    namespace impl 
    { 
     using namespace otherlibrary; 
     using namespace boost; 
     using namespace std; 
     using namespace whatever::floats::your::boat; 

     class myclass; 
     class myotherclass; 
    }; 
    using impl::myclass; 
    using impl::myotherclass; 
    }; 
}; 

在源:

#include <mylibrary.h> 
using namespace myproject::mylibrary; //clean! 
+0

这个接缝就像一个很好的解决方案。我不明白Maxim的评论/笑话的行数。 –

+0

我明白了。你得到2倍的函数原型的线。一个用于一个函数的原型,另一个用于“使用impl :: function”:(尽管它对类很好 –