关于类:C ++中的“ Helper”函数

关于类:C ++中的“ Helper”函数

'Helper' functions in C++

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

这促使我思考将"辅助"功能组合在一起的最佳方法。 Java / C#方式将是使用带有私有构造函数的静态函数类,例如:

1
2
3
4
5
6
7
8
class Helper  
{  
private:  
  Helper() { }
public:  
  static int HelperFunc1();  
  static int HelperFunc2();  
};

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

1
2
3
4
5
namespace Helper  
{  
  int HelperFunc1();  
  int HelperFunc2();  
}

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


开销不是问题,尽管名称空间具有一些优势

  • 您可以在另一个标题中重新打开一个命名空间,从而在逻辑上对内容进行分组
    保持较低的编译依赖性
  • 您可以使用名称空间别名来发挥自己的优势
    (调试/发行版,特定于平台的助手,..)

    例如我已经做了类似的事情

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    namespace LittleEndianHelper {
       void Function();
    }
    namespace BigEndianHelper {
       void Function();
    }

    #if powerpc
       namespace Helper = BigEndianHelper;
    #elif intel
       namespace Helper = LittleEndianHelper;
    #endif

当人们需要一种类型时,可能会在namespace上使用class(或struct)的情况,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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;
}


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//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);
};

还有命名空间...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//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 ++中则不能进行声明)。


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

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

在类和名称空间中使用它们不会有任何额外的开销。


我如何在C ++中正确使用名称空间的答案的复制/修剪/重做部分。

使用"使用"

您可以使用"使用"来避免重复使用辅助功能的"前缀"。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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的" C ++编程语言"中找到。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
namespace AAA
{
   void doSomething() ;
}

namespace BBB
{
   void doSomethingElse() ;
}

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

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

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


命名空间提供了Koenig查找的其他优点。使用帮助程序类可能会使您的代码更加冗长-通常,您需要在调用中包括帮助程序类的名称。

命名空间的另一个好处是以后的可读性。使用类时,您需要包含" Helper"之类的词,以提醒您以后该特定类不用于创建对象

实际上,两者都没有开销。编译后,仅使用的名称改写有所不同。


创建辅助函数时,我倾向于使用匿名名称空间。由于(通常)仅应由关心它们的模块看到它们,因此这是控制依赖项的好方法。


推荐阅读