10

每次我有一些“实用”方向的功能时,我最终都会想知道哪个选项是最好的。例如,打印消息结构(自己的或外部的)、一些编码/解码代码或只是我正在工作的上下文中的一些有用的转换函数。

我考虑的选项是:

1) 辅助类/结构中的静态函数。

struct helper
{
    static bool doSomething(...);
};

2) 非成员函数。

namespace helper
{
    bool doSomething(...);
}

3) 静态非成员函数。

namespace helper
{
    static bool doSomething(...);
}

在某些情况下,可能需要在“实用程序”中初始化或保持状态,所以我选择选项 1 以避免“全局”状态。但是,如果没有需要保留的状态,我应该选择 2 还是 3?选项2和3之间的实际区别是什么?

有什么重要的考虑因素,有没有首选的方法来解决这个问题?谢谢!

4

3 回答 3

13

选项 2 和 3 之间的区别在于,在第二种情况下,该函数将位于翻译单元的内部。如果该函数仅在 cpp 中定义,这应该是选项(大致相当于一个未命名的命名空间——这是要考虑的第四个选项,再次大致相当于 3)。

如果该函数将由不同的翻译单元使用,那么您应该使用选项 2。该函数将被编译一次(除非您将其标记为inline并在头文件中提供定义),而使用选项 3,编译器将创建它每个翻译单元的内部副本。

对于选项 1,我会避免它。在 Java 或 C# 中,您被迫在任何地方使用类,并且当操作不能很好地映射到对象范例时,您最终会使用实用程序类。另一方面,在 C++ 中,您可以将这些操作作为独立函数提供,而无需添加额外的层。如果您选择实用程序类,请不要忘记禁用对象的创建。

函数是在类还是命名空间级别会影响查找,这将影响用户代码。静态成员函数需要始终使用类名进行限定,除非您在类范围内,而将命名空间函数带入范围有不同的方法。作为一个说明性示例,考虑一堆数学辅助函数和调用代码:

double do_maths( double x ) {
   using namespace math;
   return my_sqrt( x ) * cube_root(x);
}
// alternatively with an utility class:
double do_maths( double x ) {
   return math::my_sqrt(x) * math::cube_root(x);
}

你觉得哪个更容易阅读是另一回事,但我更喜欢前者:在函数中,我可以选择命名空间,然后只关注操作而忽略查找问题。

于 2011-08-05T08:19:10.630 回答
10

不要将类用作命名空间。在命名空间内使用免费(即非成员)函数是最简单的事情

此外,如果必须跨多个翻译单元使用该函数,则它不应该是静态的。否则,它将被复制。

使用类作为命名空间是愚蠢的:为什么要创建一个你不会实例化的类?

如果要保留某些状态,则在某处具有全局状态:函数内部的静态变量,实用程序全局对象(可能在“私有”命名空间中,或作为其中一个翻译单元中的静态全局变量)。不要编写你没有实例化的类。

唉,有 C#/Java 背景的人习惯做这种愚蠢的事情,但这是因为他们的语言设计者单方面认定自由函数是邪恶的。他们是否应该被枪杀是宗教问题。

最后提醒一句:应该很少使用全局状态。实际上,它以一种您无法控制代码何时增长的方式将您的代码与全局状态的存在联系起来。您应该始终问自己为什么不明确说明这种耦合。

于 2011-08-05T08:08:27.363 回答
-1

我将 struct 与静态函数一起使用,因为它比命名空间中的非成员函数提供了更好的隔离(由于避免了 Koenig 查找)。

举一个我想避免的例子:

namespace Foo
{
      struct A
      {
      };

      void f(const A& a) {}
}

void f(const Foo:A& a) { std::cout << "AAA"; }

int main(void)
{
      Foo::A a;
      f(a); // calls Foo:f
      return 0;
}
于 2011-08-05T08:17:16.340 回答