30

在重构一些旧代码时,我删除了一些实际上应该是静态的公共方法,因为它们 a) 不对任何成员数据进行操作或调用任何其他成员函数,并且 b) 因为它们可能在其他地方证明是有用的。

这让我想到了将“帮助”功能组合在一起的最佳方式。Java/C# 方法是使用带有私有构造函数的静态函数类,例如:

class Helper  
{  
private:  
  Helper() { }
public:  
  static int HelperFunc1();  
  static int HelperFunc2();  
};

但是,作为 C++,您也可以使用命名空间:

namespace Helper  
{  
  int HelperFunc1();  
  int HelperFunc2();  
}

在大多数情况下,我认为我更喜欢命名空间方法,但我想知道每种方法的优缺点是什么。例如,如果使用类方法,会有任何开销吗?

4

7 回答 7

38

开销不是问题,虽然命名空间有一些优势

  • 您可以在另一个标头中重新打开命名空间,更合理地对事物进行分组,同时保持编译依赖性低
  • 您可以使用命名空间别名来发挥自己的优势(调试/发布、特定于平台的帮助程序,......)

    例如,我做过类似的事情

    namespace LittleEndianHelper {
       void Function();
    }
    namespace BigEndianHelper {
       void Function();
    }
    
    #if powerpc
       namespace Helper = BigEndianHelper;
    #elif intel
       namespace Helper = LittleEndianHelper;
    #endif
    
于 2008-09-17T06:57:30.913 回答
11

可能使用class(或struct)overnamespace的一种情况是需要一种类型,例如:

struct C {
  static int f() { return 33; }
};

namespace N {
  int f() { return 9; }
}

template<typename T>
int foo() {
  return T::f();
}

int main() {
  int ret = foo<C>();
//ret += foo<N>(); // compile error: N is a namespace
  return ret;
}
于 2008-09-18T00:40:24.930 回答
6

为了增加 Pieter 的出色响应,名称空间的另一个优点是您可以转发声明您放在其他地方的名称空间中的东西,尤其是结构......

//Header a.h
// Lots of big header files, spreading throughout your code
class foo
{
  struct bar {/* ... */);
};

//header b.h
#include a.h // Required, no way around it, pulls in big headers
class b
{
  //...
  DoSomething(foo::bar);
};

并使用命名空间...

//Header a.h
// Big header files
namespace foo
{
  struct bar {/* ... */);
}

//header b.h
// Avoid include, instead forward declare 
//  (can put forward declares in a _fwd.h file)
namespace foo
{
  struct bar;
}

class b
{
  //...
  // note that foo:bar must be passed by reference or pointer
  void DoSomething(const foo::bar & o);
};

一旦您最终得到一个跨越数百个源文件的项目,前向声明会对您的编译时间产生很大的影响。

从 paercebal 编辑

答案太好了,不能因为枚举错误而死(见评论)。我用结构替换了枚举(只能在 C++0x 中前向声明,而不是在今天的 C++ 中)。

于 2008-09-17T07:30:11.477 回答
4

使用命名空间的主要优点是您可以稍后重新打开它并添加更多内容,而您不能使用类来做到这一点。这使得这种方法更适合松散耦合的帮助程序(例如,您可以为整个库拥有一个 Helpers 命名空间,就像所有 STL 都在 ::std 中一样)

类的主要优点是你可以将它嵌套在使用它的类中,你不能在类中嵌套命名空间。这使得这种方法更适合紧密耦合的助手。

将它们放在类与命名空间中不会有任何额外的开销。

于 2008-09-17T06:55:00.780 回答
3

命名空间提供了 Koenig 查找的额外优势。使用辅助类可能会使您的代码更加冗长——您通常需要在调用中包含辅助类名称。

命名空间的另一个好处是以后的可读性。对于类,您需要包含诸如“Helper”之类的词,以便稍后提醒您特定类不用于创建对象

在实践中,两者都没有开销。编译后,只有使用的名称 mangling 不同。

于 2008-09-17T09:05:24.787 回答
3

在创建辅助函数时,我倾向于使用匿名命名空间。因为它们应该(通常)只被关心它们的模块看到,所以它是控制依赖关系的好方法。

于 2008-09-18T13:27:24.587 回答
3

复制/修剪/修改了我的答案的一部分,您 如何在 C++ 中正确使用命名空间?.

使用“使用”

您可以使用“使用”来避免重复辅助函数的“前缀”。例如:

struct AAA
{
   void makeSomething() ;
} ;

namespace BBB
{
   void makeSomethingElse() ;
}

void willCompile()
{
   AAA::makeSomething() ;
   BBB::makeSomethingElse() ;
}

void willCompileAgain()
{
   using BBB ;

   makeSomethingElse() ; // This will call BBB::makeSomethingElse()
}

void WONT_COMPILE()
{
   using AAA ; // ERROR : Won't compile

   makeSomething() ; // ERROR : Won't compile
}

命名空间组合

命名空间不仅仅是包。另一个例子可以在 Bjarne Stroustrup 的“The C++ Programming Language”中找到。

在“特别版”的8.2.8 Namespace Composition中,他描述了如何将两个命名空间 AAA 和 BBB 合并到另一个名为 CCC 的命名空间中。因此 CCC 成为 AAA 和 BBB 的别名:

namespace AAA
{
   void doSomething() ;
}

namespace BBB
{
   void doSomethingElse() ;
}

namespace CCC
{
   using namespace AAA ;
   using namespace BBB ;
}

void doSomethingAgain()
{
   CCC::doSomething() ;
   CCC::doSomethingElse() ;
}

您甚至可以从不同的命名空间导入选择符号,以构建您自己的自定义命名空间接口。我还没有找到它的实际用途,但从理论上讲,它很酷。

于 2008-12-20T14:30:07.397 回答