类VERSUS命名空间,OR类AND命名空间?

时间:2013-01-19 21:15:28

标签: c++ class namespaces static-methods non-member-functions

Class和Namespace?

这个问题是关于我看到自己越来越多地使用的模式:为相关概念提供类和命名空间。我认为这主要是由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中的自由功能栏,即:: Foo :: bar(),或者类中的成员函数Foo in namespace :: 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的构造函数:

::富::富::美孚()    VS    :: Foo_ns ::富::美孚()

后者并没有好多少,但有点不那么混乱。

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

结论

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

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

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

或者我刚刚因为这样做而被打破了?

3 个答案:

答案 0 :(得分:11)

我建议将您的类放在具有相关功能的命名空间中。其中一个重要原因是argument-dependent lookup(ADL)。当您调用具有类类型参数的非成员函数时,将在该类的封闭名称空间中查找函数名称。所以,如果你有,请说:

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

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

foo::bar x;
baz(x);

如果没有限定baz,编译器仍然会找到该函数,因为它位于包含其参数类型的命名空间内。通过这种方式,C ++认为类的封闭命名空间的内容是该类接口的一部分。以这种方式将作为类的接口的一部分的函数实现为非成员非朋友函数,允许ADL查找函数increases encapsulation。如果函数不需要访问类的内部,请不要使它成为该类的成员 - 而是将其放在与该类相同的命名空间中。

但是,您的命名空间与您的类具有相同的名称是可疑的。通常,命名空间中将有多个类,即使没有,名称也会不同。命名空间的名称应描述其内容,而不仅仅是其中的单个类。

答案 1 :(得分:4)

单独的 Foo_ClassFoo_Namespace肯定是错误的,它会阻止您使用Argument Dependent Lookup查找命名空间中与该类一起使用的函数

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

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

使用初始大写字母命名命名空间也很不寻常。如果您的类名以大写字母开头,则可以使用名称空间foo和类Foo,并使用foo::Foo

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

我认为课程上的_c后缀非常荒谬。我不喜欢命名空间上的_ns后缀,它唯一的优点是Foo_ns::Foo优于Foo::Foo,但我仍然会改为foo::Foo

(你的问题的其余部分似乎是“为什么命名空间好”,这不应该真正需要解释,它们被添加到语言中是有原因的,并且建议使用非成员来部分不需要成员的界面.Google是正确的,通常使用头文件而不是前向声明,这不会改变你可以在单独的头文件中重新打开命名空间以向命名空间添加新名称的事实,这就是你所描述的优势。)

答案 2 :(得分:2)

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

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

枚举类型称为status::type,可以使用status::okay之类的值。相当可读的恕我直言。