静态、非成员或静态非成员函数?

发布于 2024-11-27 19:54:22 字数 643 浏览 0 评论 0原文

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

我想到的选项是:

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

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

2)非成员函数。

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

3)静态非成员函数。

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

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

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

Every time I have some functionality which is in the direction of "utility", I end up wondering which option is the best. For instance, printing message structs (own or external), some encoding/decoding code or simply a few useful conversion functions in the context I'm working.

The options I think about are:

1) Static function in helper class/struct.

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

2) Nonmember function.

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

3) Static nonmember function.

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

In some cases there might be necessary to initialize or keep state in the "utility", so then I go for option 1 to avoid "global" state. However, if there is no state that needs to be kept, should I then option 2 or 3? What's the practical difference between option 2 and 3?

What is important to consider and is there a preferred way to approach this? Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

樱娆 2024-12-04 19:54:22

选项 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);
}

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

The difference between options 2 and 3 is that in the second case, the function will be internal to the translation unit. If the function is only defined in the cpp this should be the option (which is roughly equivalent to an unnamed namespace --which is a fourth option to consider, again roughly equivalent to 3).

If the function is to be used by different translation units then you should go with option 2. The function will be compiled once (unless you mark it as inline and provide the definition in the header), while with option 3 the compiler will create it's an internal copy in each translation unit.

As of option 1, I would avoid it. In Java or C# you are forced to use classes everywhere, and you end up with utility classes when operations do not nicely map to the object paradigm. In C++ on the other hand, you can provide those operations as free standing functions, and there is no need to add the extra layer. If you opt for the utility class, don't forget to disable the creation of the objects.

Whether the functions are at class or namespace level will affect the lookup, and that will impact on user code. Static member functions need to be qualified with the class name always, unless you are inside the class scope, while there are different ways of bringing namespace functions into scope. As an illustrative example, consider a bunch of math helper functions, and calling code:

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);
}

Which one you find easier to read is a different story, but I prefer the former: within the function I can select the namespace and then just focus on the operations and ignore lookup issues.

許願樹丅啲祈禱 2024-12-04 19:54:22

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

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

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

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

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

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

Don't use classes as namespaces. Using a free (ie. nonmember) function inside a namespace is the simplest thing.

Moreover, the function shouldn't be static if it has to be used across multiple translation units. Otherwise, it will be duplicated.

Using classes as namespace is stupid: why make a class which you won't instantiate ?

If some state is to be kept, then have a global state somewhere: static variable inside the function, utility global object (possibly in a "private" namespace, or as a static global variable in one of your translation units). Don't write classes you don't instantiate.

Alas, people with a C#/Java background are used to do this stupid thing, but it is because their language designers have decided unilaterally that free functions are evil. Whether they should be shot or not is a matter of religion.

As a last word of caution: global state should be rarely used. Indeed, it couples your code to the existence of the global state in a way you don't control when the code grows. You should always ask yourself why you don't make this coupling explicit.

月下客 2024-12-04 19:54:22

我将 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;
}

I use struct with static functions, because it offers a slighly better isolation than non-member functions in namespaces (due to the avoidance of Koenig lookup).

To give an example of the thing I want to avoid:

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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文