命名空间+函数与类的静态方法
比方说,我有或将要写一组相关的function。 假设他们是math相关的。 在组织上,我应该:
- 写这些函数,把它们放在我的
MyMath
命名空间,并通过MyMath::XYZ()
引用它们 - 创build一个名为
MyMath
的类,并使这些方法是静态的,并引用类似的MyMath::XYZ()
为什么我会select一个作为组织软件的手段?
默认情况下,使用名称空间函数。
类是build立对象,而不是replace名称空间。
面向对象的代码
斯科特·迈耶斯(Scott Meyers)为他的Effective C ++书中的这个主题写了一个完整的条目,“优先考虑对成员函数的非成员非好友函数”。 我在Herb Sutter的一篇文章中发现了这个原理的在线参考: http : //www.gotw.ca/gotw/084.htm
要知道的重要的事情是: 在C ++中,与同一个名称空间中的函数属于该类的接口 (因为ADL将在parsing函数调用时search这些函数)。
命名空间函数,除非声明为“朋友”,否则不能访问类的内部,而静态方法。
这意味着,例如,当维护你的类时,如果你需要改变你的类的内部,你将需要在它的所有方法中search副作用,包括静态方法。
扩展名I
将代码添加到类的接口。
在C#中,即使您没有访问权限,也可以将方法添加到类中。 但在C ++中,这是不可能的。
但是,仍然在C ++中,您仍然可以添加一个名称空间函数,甚至可以添加到某人为您写的一个类中。
从另一方面来看,在devise代码时这很重要,因为通过将函数放入命名空间,您将授权用户增加/完成类的接口。
扩展II
前一点的副作用是不可能在多个头文件中声明静态方法。 每个方法都必须在同一个类中声明。
对于名称空间,可以在多个头文件中声明来自同一个名称空间的函数(几乎标准的交换函数就是最好的例子)。
扩展III
命名空间的基本缺点是,在某些代码中,如果使用关键字“using”,则可以避免提及它:
#include <string> #include <vector> // Etc. { using namespace std ; // Now, everything from std is accessible without qualification string s ; // Ok vector v ; // Ok } string ss ; // COMPILATION ERROR vector vv ; // COMPILATION ERROR
你甚至可以把“污染”限制在一个等级上:
#include <string> #include <vector> { using std::string ; string s ; // Ok vector v ; // COMPILATION ERROR } string ss ; // COMPILATION ERROR vector vv ; // COMPILATION ERROR
这个“模式”对于正确使用几乎标准的交换习惯用法是强制性的。
这是不可能的类与静态方法。
所以,C ++名称空间有自己的语义。
但是更进一步,您可以像inheritance一样组合命名空间。
例如,如果您有一个带有函数AAA的名称空间A和一个带有函数BBB的名称空间B,则可以声明一个名称空间C,并将AAA和BBB带入该名称空间中,并使用关键字using。
结论
命名空间是用于命名空间的。 类是为了类。
C ++被devise成每个概念都是不同的,在不同的情况下用不同的方式来解决不同的问题。
当你需要命名空间时不要使用类。
在你的情况下,你需要命名空间。
有很多人会不同意我的看法,但这是我的看法:
一个类本质上是某种对象的定义。 静态方法应定义与该对象定义密切相关的操作。
如果你只是要有一组相关的函数不与一个底层对象或某种对象的定义相关联,那么我只能说只与一个名称空间。 就概念而言,对我来说,这是一个更明智的做法。
例如,就你的情况来说,问自己:“什么是MyMath?” 如果MyMath
没有定义一种对象,那么我会说:不要把它作为一个类。
但就像我说的,我知道有很多人会(甚至是强烈的)不同意我(尤其是Java和C#开发人员)。
- 如果您需要静态数据,请使用静态方法。
- 如果它们是模板函数,并且希望能够为所有函数一起指定一组模板参数,那么在模板类中使用静态方法。
否则,使用名称空间函数。
回应评论:是的,静态方法和静态数据往往被过度使用。 这就是为什么我只提供两个相关的场景,我认为他们可以帮助。 在OP的具体例子(一组math例程)中,如果他想要指定参数的能力 – 比如核心数据types和输出精度 – 可以应用于所有例程,他可能会这样做:
template<typename T, int decimalPlaces> class MyMath { // routines operate on datatype T, preserving at least decimalPlaces precision }; // math routines for manufacturing calculations typedef MyMath<double, 4> CAMMath; // math routines for on-screen displays typedef MyMath<float, 2> PreviewMath;
如果你不需要,那么通过一切手段使用一个名称空间。
你应该使用一个名字空间,因为名字空间比一个类有许多优点:
- 您不必在同一个标题中定义所有内容
- 你不需要在头文件中公开你所有的实现
- 你不能
using
class级成员; 您可以using
名称空间成员 - 你不能
using class
,尽pipeusing namespace
并不是一个好主意 - 使用一个类意味着有一些对象在没有的时候被创build
静态成员,在我看来,是非常非常滥用。 在大多数情况下,它们并不是真正的必需品。 静态成员函数可能最好作为文件作用域函数,而静态数据成员只是具有更好的,不适当的声誉的全局对象。
我更喜欢命名空间,这样你可以在实现文件中的匿名命名空间中拥有私有数据(所以它不必显示在标题中而不是private
成员)。 另一个好处是通过using
你的命名空间,方法的客户端可以select不指定MyMath::
名称空间和类方法都有其用途。 命名空间能够跨文件传播,但如果您需要强制所有相关的代码进入一个文件,这是一个弱点。 如上所述,类还允许您在类中创build私有静态成员。 你可以把它放在实现文件的匿名命名空间中,但是它的范围比在类里面有更大的范围。
还有一个使用类的理由 – 选项使用访问说明符。 然后你可以将你的公共静态方法分解成更小的私有方法。 公共方法可以调用多个私有方法。