12

类和命名空间?

这个问题是关于我看到自己越来越多地使用的一种模式:同时拥有相关概念的类和命名空间。我认为这主要是由 C++ 语言工件推动的,但并非完全如此。

我想最重要的问题是:这是个好主意吗?同时拥有相关概念的类和命名空间?

低级问题:

做这个的最好方式是什么?

嵌套在命名空间中的类?:

namespace Foo_Namespace {
   class Foo_Class {
       ...
   };
}

还是分开,对等,类和命名空间?:

class Foo_Class {
    ...
};
namespace Foo_Namespace {
   // non-class free functions, etc.
}

我必须承认我倾向于将类嵌套在命名空间中。即使它导致丑陋的名字。但即使我这样做,我应该使用什么命名约定:

以下内容太长,导致名称非常难看 Foo_Namespace::Foo_Class

namespace Foo_Namespace {
   class Foo_Class {
       ...
   };
}

不必在名称中使用任何后缀或指示符:

namespace Foo {
   class Foo {
       ...
   };
}

但是后来我发现自己不确定,当我查看 Foo::bar() 时,它是命名空间 ::Foo 中的自由函数 bar,即 ::Foo::bar(),还是命名空间中类 Foo 中的成员函数::Foo::Foo::bar()。

像 ::Foo::Foo::bar 这样的名字仍然不是,嗯,很好。

目前我正在做

不必在名称中使用任何后缀或指示符:

namespace Foo_ns {
   class Foo {
       ...
   };
}

主要是因为我通常先创建类,然后才意识到命名空间会很好。

我想知道我是否应该恢复多年来未使用的命名约定:_c 用于类,_ns 用于命名空间:

namespace Foo_ns {
   class Foo_c {
       ...
   };
}

细节:

我不会重复我上面所说的,但我会补充一点。

除了类之外,我知道使用命名空间的最实际原因是您可以在命名空间中对自由函数进行前向声明,但不允许对类的某些方法进行前向声明。即对于一个类,您必须全部或全部声明它。而对于一般的自由函数,特别是命名空间中的自由函数,您可以将其声明为零碎的。部件可以在不同的头文件中声明。您可以只为一个或两个函数使用前向声明,而不是为一个庞大的类#include 一个庞大的仅标头库。等等。

(例如,请参阅http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Forward_Declarations,尽管 Google 完全反对前向声明而不是包含标题。)

另一个原因是命名空间允许类本身保持较小。

类的最大优点是类可以传递给模板,而命名空间不能。

我更喜欢将类嵌套在命名空间 Foo_ns/Foo_ns::Foo_c 中,而不是将它们作为对等的 Foo_ns/Foo_c,因为通常一个类需要辅助类,例如 Foo_ns/Foo_ns::Foo_c/Foo_ns::Foo_Helper_c。如果类和命名空间是对等的,那么拥有 Foo_ns/Foo_c 而拥有 Foo_ns::Foo_Helper_c 似乎很奇怪。

我喜欢命名空间,因为我同意 Andrei Alexandresciu: http: //laser.inf.ethz.ch/2012/slides/Alexandrescu/1-C++%20course%20parts%201%20and%202.pdf

 Class methods vs. free functions

 • Conventional wisdom: methods are cool, free functions are so 1960s
 • Yet:
   ◦ Free functions improve encapsulation over methods
   ◦ Free functions may be more general
   ◦ Free functions decouple better
   ◦ Free functions support conversions on their left-hand argument

  Surprising fact #2

    Making a function a method should be
    your last, not first, choice

  (c) 2012– Andrei Alexandrescu. 32 / 59

创建自由函数比创建类中的方法更好。

但有时只需要使用类——例如用于模板。

命名约定明智

我过去使用过 Foo_ns / Foo_ns::Foo_c

我现在正在使用 Foo_ns / Foo_ns::Foo

(顺便说一句,我倾向于使用 Class_Names_With_Underscores_and_Initial_Caps,而不是 CamelCase。)

如果有意义,我可能会省略命名空间上的 _ns 后缀 - 例如,命名空间和类不需要具有相同的名称。

我不喜欢让他们有相同的名字。考虑命名空间 foo 中类 Foo 的构造函数:

::Foo::Foo::Foo() 与 ::Foo_ns::Foo::Foo()

后者也好不了多少,但也少了一些混乱。

我认为我通常自己创建类,而不将其嵌套在命名空间中。事实上,在我意识到嵌套在命名空间中的类会更好之前,我可能会添加一些静态方法。到那个阶段,重构可能会很痛苦,有时我最终会创建转发函数,从类静态方法转发到命名空间中的自由函数,反之亦然。这让我很遗憾机器人从第 1 步开始就跳到带有命名空间的类。

结论

我当前的 BKM 是 Foo_ns / Foo_ns::Foo,即

namespace Foo_ns {
   class Foo { 
   ...
   };
}

我会很感激任何建议,任何改进。

还是我只是因为这样做而崩溃?

4

3 回答 3

12

我建议将您的类放在具有相关功能的命名空间中。造成这种情况的一个重要原因是依赖于参数的查找(ADL)。当您调用具有类类型参数的非成员函数时,会在该类的封闭命名空间中查找函数名称。因此,如果您有,请说:

namespace foo {
  class bar { };
  void baz(bar);
}

如果你想调用baz,你不需要明确地给出它包含的命名空间,你可以简单地做:

foo::bar x;
baz(x);

没有限定baz,编译器仍然找到该函数,因为它位于包含其参数类型的命名空间内。通过这种方式,C++ 将类的封闭命名空间的内容视为该类接口的一部分。以这种方式将属于类接口一部分的函数实现为非成员非友元函数,允许 ADL 找到该函数,增加了封装性。如果一个函数不需要访问一个类的内部,不要让它成为该类的成员 - 相反,将它放在与该类相同的命名空间中。

但是,您的命名空间与您的类同名是值得怀疑的。通常在一个命名空间中会有多个类,即使没有,名称也会不同。命名空间的名称应该描述其内容,而不仅仅是其中的单个类。

于 2013-01-19T21:27:33.523 回答
4

分开 Foo_Class并且Foo_Namespace绝对是错误的,它会阻止您使用 Argument Dependent Lookup 在命名空间中查找打算与类一起使用的函数。

因此,如果命名空间中有函数采用类类型的参数,则该类应该嵌套在命名空间中。

对类和命名空间使用相同的名称有点令人困惑,在某些情况下可能会导致歧义。

使用首字母大写字母命名命名空间也是不常见的。如果您的类名以大写字母开头,您可以使用 namespacefoo和 class Foo,给出foo::Foo.

命名空间不会包含多个类吗?这对我来说听起来很不寻常。我会在其所有内容之后命名命名空间,而不是一种类型。例如,如果它是一个socket类,我会将它放在一个networking命名空间中。

我认为_c类的后缀是完全荒谬的。我也不喜欢_ns命名空间上的后缀,它唯一的优点Foo_ns::Foo就是比它更好,Foo::Foo但我仍然会这样做foo::Foo

(你的问题的其余部分似乎是“为什么命名空间好”,这不应该真的需要解释,它们被添加到语言中是有原因的,以及对不使用的部分接口使用非成员的好建议'不需要成为成员。谷歌是对的,通常使用标题而不是前向声明,这不会改变您可以在单独的标题中重新打开命名空间以向命名空间添加新名称的事实,这是您的优势重新描述。)

于 2013-01-19T21:25:03.773 回答
2

我不喜欢重复事物的“分形”名称并尽量避免它。对于你的情况,我会尝试命名空间“Foo”和嵌套类“Type”。也许其他名称会更合适,但这取决于您的实际用例。这是一个我发现自己一遍又一遍地重复的用例:

namespace status {
    enum type {
        invalid,
        okay,
        on_fire
    };
    // debug output helper
    char const* to_string(type t);
}

枚举类型称为status::type,它可以取值如status::okay. 非常易读恕我直言。

于 2013-01-19T21:28:32.970 回答