C ++名称空间建议


75

我只是自学C ++命名空间(来自C#背景),并且我真的开始认为,即使C ++比所有其他语言都有更好的功能,嵌套命名空间也不是其中之一!

我是否正确认为,为了声明一些嵌套的名称空间,我必须执行以下操作:

namespace tier1
{
    namespace tier2
    {
        namespace tier3
        {
            /* then start your normal code nesting */
        }
    }
}

相对于:

namespace tier1::tier2::tier3
{
}

C#?

当我需要转发声明时,这会变得更加疯狂:

namespace tier1
{
    namespace tier2
    {
        namespace forward_declared_namespace
        {
            myType myVar; // forward declare
        }
        namespace tier3
        {
            /* then start your normal code nesting */
            class myClass
            {
                forward_declared_namespace::myType myMember;
            }
        }
    }
}

请记住,我开发的典型系统包括:

MyCompany::MySolution::MyProject::System::[PossibleSections]::Type

这就是为什么您不会在C ++示例中看到太多使用命名空间的原因吗?还是通常只有单个(非嵌套)名称空间?

更新

对于任何有兴趣的人,这就是我最终解决此问题的方式。


8
最后,C ++ 17将允许namespace tier1::tier2::tier3
underscore_d

7
更新中的链接已损坏...
AlwaysLearning '16

2
我有兴趣了解您如何解决该问题,并单击了链接。Always,正如AlwaysLearning所指出的那样,链接已断开。
csj

Answers:


113

C ++名称空间并非旨在成为一种设计机制,它们只是为了防止名称冲突。您确实不希望或不需要在99.99%的情况下使用嵌套名称空间。

在C ++中正确使用名称空间的一个很好的例子是C ++标准库。这个相当大的库中的所有内容都放在一个名为std的命名空间中-无需尝试或需要将库分解为(例如)I / O子命名空间,数学子命名空间,容器子命名空间等等

C ++中建模的基本工具是类(在某种程度上是模板),而不是名称空间。如果您感到需要嵌套,则应考虑使用嵌套类,它们比名称空间具有以下优点:

  • 他们有方法
  • 他们可以控制访问
  • 他们不能重新打开

考虑了这些因素之后,如果您仍然希望通过各种方式使用嵌套的名称空间,则以这种方式使用它们在技术上没有错。


18
模板并非旨在作为mpl机制。但是,Guys以这种方式使用它们。
Mykola Golubyev

13
嗯 。。我讨厌成为一个学徒(我们都知道这是个谎言),但是<iostream>不能在std名称空间内实现ios名称空间吗?我目前没有一个C ++编译器。。。
Binary Worrier 2009年

1
您能否提供参数(而非示例),为什么好主意不使用嵌套名称空间?
2009年

4
@binary IOS为basic_ios <炭>一个typedef

7
C ++中的命名空间具有特定功能,以便以更加模块化的方式支持接口。请参考Herb Sutter和Andrei Alexandrescu撰写的出色著作“ C ++编码标准”中的以下规则:57:将类型及其非成员函数接口保留在同一名称空间中。58:将类型和函数保存在单独的命名空间中,除非专门用于协同工作。
alexk7 2010年

23

C ++名称空间是对先前产品的巨大改进(即完全没有名称空间)。C#命名空间扩展了该概念并对其进行了应用。我建议您将名称空间保持在简单的平面结构中。

编辑 您是否建议我由于此处概述的不足之处?

只是“是”。C ++名称空间并非旨在帮助您像在C#中那样对逻辑和库进行分区。

C ++名称空间的目的是阻止C开发人员遇到的现实问题,即当使用两个导出相同函数名的第三方库时,他们会遇到名称冲突。C开发人员对此有各种解决方法,但这可能会很痛苦。

这个想法是STL等具有std::名称空间,“ XYZ Corp”提供的库将具有xyz::名称空间,为“ ABC corp”工作的人会将所有内容都放在一个abc::名称空间中。


2
您是否建议由于我在此处概述的缺点?
亚当·内洛

如果许多人在“ ABC corp”那里为许多项目工作,并且多年来,项目合并或拆分,那么至少拥有abc::PROJECT子命名空间是很有意义的。如果较大的项目有明确的部分,那么abc::PROJECT::SECTION也很有意义。甚至C ++的上帝都知道,有部分,并创建了类似std::chrono或的子命名空间std::ios
Kai Petzke

19

我在进行前向声明时的操作如下所示:

 namespace abc { namespace sub { namespace subsub { class MyClass; }}}

我的前向声明折叠成一行。牺牲前向声明的可读性,以换取其余代码的可读性。对于定义,我也不使用缩进:

 namespace abc {
 namespace sub {
 namespace subsub {
 
 class MyClass 
 {
    public:
       MyClass();

       void normalIntendationsHere() const;
 };

 }
 }
 }

使用这种样式在开始时需要一点纪律,但这对我来说是最好的折衷方案。


从C ++ 17开始,您可以使用问题作者提出的语法声明名称空间。

namespace A::B::C { ... }

嵌套名称空间定义:namespace A::B::C { ... }等效于namespace A { namespace B { namespace C { ... } } }

https://en.cppreference.com/w/cpp/language/namespace


7
这不是对所提问题的答案。
Paul Merrill

2
@Jammer:那时候我是stackoverflow的新手,却不知道这个主意。
doc 2012年

8

Atleast是一个小的帮助,在某些情况下,您可以这样做:

namespace foo = A::B::C::D;

然后将A :: B :: C :: D称为foo。但仅在某些情况下。


2
在什么情况下您不能这样做?
Michael Dorst

问题中的情况是一个。我认为?
Lightness Races in Orbit

6

您可以跳过缩进。我经常写

namespace myLib { namespace details {

/* source code */

} } /* myLib::details */

C ++源代码最终被编译成二进制,这与保留在二进制中的C#/ Java不同。因此,名称空间只是为变量命名冲突提供了一个很好的解决方案。它不适用于类层次结构。

我经常在代码中保留一两个名称空间级别。


4

首先,您可以避免名称空间缩进,因为没有理由这样做。

在示例中使用名称空间不会显示名称空间的功能。他们的力量对我来说是将领域区域彼此划分。将实用程序类与业务类分开。

只是不要在一个.h文件中混合使用不同的名称空间层次结构。命名空间是函数声明接口的一种额外注释。查看名称空间和类名称应该可以解释很多内容。

namespace product
{
namespace DAO
{

class Entity
{
};

3
“首先,您可以避免名称空间缩进,因为没有理由。” 那正是我实际上所做的;)
Adam Naylor

很好,我认为使用CASE工具可视化代码库关系和层时,命名空间的功能会进一步增强。诸如CppDepend或Visual Studio的Dependency Graph之类的工具采用了此类开发人员构建的“代码可理解性夹点”,这些夹点通过所有名称空间嵌套进行结构化,从而以结构化的一致方式真正阐明了层和依赖性。
jxramos 2014年

0

您过度使用了它们(您将一无所获)。


15
分层的有序结构。(例如“您将一无所获”)我认为这是巨大的,并且在C ++中被大大低估了。只要考虑一下这对文档有何影响。
康拉德·鲁道夫

1
@Konrad,我相信这是我问题的实质。
亚当·内罗尔

问题很老,但今天对我来说仍然很重要。我和@KonradRudolph有相同的看法,但是后来我发现嵌套的名称空间毫无价值。最好的方法是只拥有一个扁平的宽库名称空间。这很可惜,但是我们现在最好。这就是std的构造方式。这种结构是有原因的-目前没有更好的方法。
Sahsahae

@Sahsahae他们绝对不是一文不值的,即使C ++标准委员会的一位高级成员也不认为(!)。但是即使那样,也只有这样:一种观点(我不同意)。我绝对不建议轻率地嵌套-但是明智地使用嵌套名称空间已在其他语言中广泛且成功地使用,并且不会表现出Titus所存在的缺陷。
康拉德·鲁道夫

@KonradRudolph这是您的问题:其他语言。这个问题严格来说是关于C ++名称空间的,我还没有扩展它的范围。有趣的是,就像C ++命名空间一样,我不小心以某种方式设法将其扩展为“所有语言的命名空间”,因为langs::cpp仍然可以看到其中的所有内容langs,而没有真正的隔离cpp……这正是C ++命名空间的问题以及为什么建议采用扁平结构。例如,在Rust模块中,您无法langs::*从访问cpp,而我从未说过这很糟糕,这很好,但是在C ++中却不是。
Sahsahae

0

我发现您可以像这样模仿c#名称空间;

namespace ABC_Maths{
    class POINT2{};
    class Complex{};
}

namespace ABC_Maths_Conversion{
    ABC_MATHS::Complex ComplexFromPOINT2(ABC_MATHS::POINT2)
    {return new ABC_MATHS::Complex();}

    ABC_MATHS::POINT4 POINT2FromComplex(ABC_MATHS::COMPLEX)
    {return new ABC_MATHS::POINT2();}
}

namespace ABC
{
}

但是代码似乎并不是很整洁。我希望使用时间长

最好在类中嵌套尽可能多的功能,例如

namespace ABC{
    class Maths{
        public:
        class POINT2{};
        class Complex:POINT2{};
        class Conversion{
            public:
            static Maths.Complex ComplexFromPOINT2(MATHS.POINT2 p)
            {return new MATHS.Complex();}

            static MATHS.POINT2 POINT2FromComplex(MATHS.COMPLEX p)
            {return new ABC::MATHS.POINT2();}// Can reference via the namespace if needed
} /*end ABC namespace*/

这仍然有些长。但是确实有点OO。

并听到它似乎做得最好

namespace ABC
{
    class POINT2{};
    class Complex:POINT2{};
    Complex ComplexFromPOINT2(POINT2 p){return new Complex();}
    POINT2 POINT2FromComplex(Complex){return new POINT2();}
}

听到用法的外观

int main()
{
    ABC_Maths::Complex p = ABC_Maths_Conversion::ComplexFromPOINT2(new ABC_MATHS::POINT2());

    // or THE CLASS WAY

    ABC.Maths.Complex p = ABC.Maths.Conversion.ComplexFromPOINT2(new ABC.Maths.POINT2());

    // or if in/using the ABC namespace

    Maths.Complex p = Maths.Conversion.ComplexFromPOINT2(new Maths.POINT2());

    // and in the final case

    ABC::Complex p = ABC::ComplexFromPOINT2(new ABC::POINT2());
}

找出为什么我从未像在c#中那样使用c ++名称空间,这很有趣。这太麻烦了,永远不会像c#名称空间那样工作。

在c ++中,名称空间的最佳用途是停止说出我的超级cout函数(每次调用时都会发出叮叮叫声)与std :: cout函数(令人印象深刻的)混为一谈。

仅仅因为c#和c ++具有命名空间,并不意味着命名空间意味着同一件事。它们不同但相似。c#名称空间的思想必须来自c ++名称空间。某人一定已经看到了类似但不同的事情可以做什么,并且没有足够的想象力来赋予它自己的原始名称(例如“ ClassPath”),这更有意义,因为它是通往类的路径而不是提供命名空间,每个空间可以具有相同的名称

我希望这可以帮助别人

我忘了说所有这些方式都是有效的,并且可以将前两种方式的适当使用并入第三种方式来创建一个有意义的库,例如(不是很好的例子)

_INT::POINT2{}

_DOUBLE::POINT2{}

因此您可以使用

#define PREC _DOUBLE 
// or #define PREC _INT 

然后为双精度POINT2创建PREC :: POINT2的实例

使用c#或java名称空间并不是一件容易的事

显然,这只是一个例子。考虑一下用法如何读取。


-1

我有时会在单独的标头中将深层名称空间声明为几个宏

名称空间

#define NAMESPACE_TIER1_TIER2_TIER3 \
    namespace tier1 { \
    namespace tier2 { \
    namespace tier3 {

#define END_NAMESPACE_TIER1_TIER2_TIER3 }}}

要在其他地方使用它:

另一个文件

#include "./namespace.h"

NAMESPACE_TIER1_TIER2_TIER3;

/* Code here */

END_NAMESPACE_TIER1_TIER2_TIER3;

宏后的多余分号是为了避免缩进。


2
这真的很麻烦,您必须为每个嵌套的命名空间定义它。另外,如果您要写NAMESPACE_TIER1_TIER2_TIER3END_NAMESPACE_TEIR1_TIER2_TIER3,为什么不只写namespace tier1{namespace tier2{namespace tier3{}}}?总共实际上更短。
Michael Dorst

我不同意,它并不短,而且会造成非常严重的缩进问题。我已经尝试过两者,并且我更喜欢#define方法。编辑:好吧,减去压痕问题...
冰淇淋

1
更喜欢您喜欢的东西,但我不建议其他用户使用。它并不短(如您从我的评论中看到的那样,两者在同一行),并且这还不包括定义,而且以这种方式跟踪错误肯定更加困难。如果打错了怎么办?编译器不会知道出了什么问题,您的错误将显示在完全错误的位置。
Michael Dorst

3
“较短”参数实际上并不成立,因为它取决于宏的定义方式。关于“ typo”的论点。输入错误的名称空间名称,您将获得与宏解决方案相同的神秘消息,但是使用宏解决方案,许多现代IDE可以解决该名称尚未定义的问题,并帮助您查找错误。名称空间名称只是自由文本,您可以在其中输入任何内容,因此实际上在避免不使用宏时很难避免输入错误。人们可以自己决定,“推荐”宏观解决方案没有任何危害。
冰淇淋”,​​2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.