每当我有一些朝向“效用”方向的功能时,我最终想知道哪个选项是最好的。例如,在我工作的上下文中打印消息结构(自己的或外部的),一些编码/解码代码或者只是一些有用的转换函数。
我想到的选项是:
1)helper class / struct中的静态函数。
struct helper
{
static bool doSomething(...);
};
2)非成员函数。
namespace helper
{
bool doSomething(...);
}
3)静态非成员函数。
namespace helper
{
static bool doSomething(...);
}
在某些情况下,可能需要在“实用程序”中初始化或保持状态,因此我选择选项1以避免“全局”状态。但是,如果没有需要保留的状态,我应该选择2还是3?选项2和3之间的实际区别是什么?
什么需要考虑重要,是否有首选方式来解决这个问题?谢谢!
答案 0 :(得分:12)
选项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);
}
你发现哪一个更容易阅读是一个不同的故事,但我更喜欢前者:在函数中我可以选择命名空间,然后只关注操作并忽略查找问题。
答案 1 :(得分:8)
不要将类用作命名空间。在命名空间内使用免费(即非成员)函数是最简单的事情。
此外,如果必须跨多个翻译单元使用该功能,则该功能不应该是静态的。否则,它将被复制。
使用类作为命名空间是愚蠢的:为什么要创建一个你不会实例化的类?
如果要保留某个状态,则在某处具有全局状态:函数内部的静态变量,实用程序全局对象(可能在“私有”命名空间中,或作为一个翻译单元中的静态全局变量)。不要编写你没有实例化的类。
唉,具有C#/ Java背景的人习惯于做这个愚蠢的事情,但这是因为他们的语言设计者单方面决定自由功能是邪恶的。他们是否应该被枪杀是一个宗教问题。最后要提醒的是:应该很少使用全球国家。实际上,它将代码与代码增长时无法控制的方式耦合到全局状态的存在。你应该总是问自己为什么不明确这种耦合。
答案 2 :(得分:-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;
}