假设我有或将要编写一组相关函数。假设它们与数学有关。在组织上,我应该:
- 编写这些函数并将它们放在我的
MyMath
名称空间中,并通过引用它们MyMath::XYZ()
- 创建一个名为的类
MyMath
,并使这些方法静态化,并类似地进行引用MyMath::XYZ()
为什么我会选择一个作为组织软件的方式?
假设我有或将要编写一组相关函数。假设它们与数学有关。在组织上,我应该:
MyMath
名称空间中,并通过引用它们MyMath::XYZ()
MyMath
,并使这些方法静态化,并类似地进行引用MyMath::XYZ()
为什么我会选择一个作为组织软件的方式?
Answers:
默认情况下,使用命名空间函数。
类是建立对象,而不是替换名称空间。
斯科特·迈耶斯(Scott Meyers)在他的《有效C ++》一书中写了整篇文章,主题是“将非成员非朋友函数优先于成员函数”。我在Herb Sutter的一篇文章中找到了对此原则的在线参考:http://www.gotw.ca/gotw/084.htm
要知道的重要一点是:在C ++中,与类位于同一名称空间的函数属于该类的接口(因为ADL在解析函数调用时将搜索这些函数)。
除非声明为“ friend”,否则命名空间函数不能访问该类的内部,而静态方法则可以。
例如,这意味着在维护类时,如果需要更改类的内部,则需要在其所有方法中搜索副作用,包括静态方法。
将代码添加到类的界面。
在C#中,即使您无权访问它,也可以将方法添加到类中。但是在C ++中,这是不可能的。
但是,即使在C ++中,您仍然可以添加命名空间函数,甚至可以添加到有人为您编写的类中。
从另一方面来看,这在设计代码时很重要,因为通过将函数放在命名空间中,您将授权用户增加/完成类的界面。
上一点的副作用是,不可能在多个标头中声明静态方法。每个方法必须在同一类中声明。
对于名称空间,可以在多个标头中声明来自同一名称空间的函数(几乎标准的交换函数就是最好的例子)。
名称空间的基本优点是,在某些代码中,如果使用关键字“ 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 ++名称空间具有自己的语义。
但这更进一步,因为您可以以类似于继承的方式组合名称空间。
例如,如果您具有一个具有功能AAA的名称空间A,具有一个具有功能BBB的名称空间B,则可以声明一个名称空间C,并使用关键字using将AAA和BBB带入该名称空间。
命名空间用于命名空间。类是针对类的。
C ++的设计使每个概念都是不同的,在不同情况下,它们的使用方式也有所不同,以解决不同的问题。
需要名称空间时不要使用类。
在您的情况下,您需要名称空间。
有很多人不同意我,但这就是我的看法:
类本质上是某种对象的定义。静态方法应定义与该对象定义密切相关的操作。
如果您只是要有一组与底层对象或某种对象的定义不相关的相关功能,那么我想说的只是使用名称空间。就我而言,从概念上讲,这要明智得多。
例如,在您的情况下,问自己:“什么是MyMath?” 如果MyMath
没有定义一种对象,那么我会说:不要使其成为一个类。
但是就像我说的那样,我知道有很多人(甚至是强烈反对)对此表示反对(特别是Java和C#开发人员)。
typedef
它们,使用它们作为模板参数,等等
否则,请使用命名空间函数。
针对评论:是的,静态方法和静态数据往往被过度使用。这就是为什么我只提供了两个相关的场景,我认为它们会有所帮助。在OP的特定示例(一组数学例程)中,如果他希望能够指定将应用于所有例程的参数(例如,核心数据类型和输出精度),那么他可以执行以下操作:
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;
如果不需要它,则一定要使用名称空间。
template
参数!
您应该使用名称空间,因为名称空间比类具有许多优势:
using
成为班级成员;您可以using
命名空间成员using class
,尽管using namespace
并非总是那么好主意我认为静态成员的使用率非常高。在大多数情况下,它们并不是真正的必需品。静态成员函数可能比文件作用域函数更好,而静态数据成员只是具有更好,当之无愧声誉的全局对象。
inline
关键字即可满足ODR。
inline
,编译器只保证一件事,而不是“内联”函数的主体。的真正(并由标准保证)的目的inline
是防止出现多个定义。阅读有关C ++的“一个定义规则”。另外,由于预编译的标头问题而不是ODR问题,因此未编译链接的SO问题。
我希望使用名称空间,这样您就可以在实现文件的匿名名称空间中拥有私有数据(因此,与private
成员相对,它根本不必显示在标头中)。另一个好处是,通过using
您的命名空间,方法的客户端可以选择不指定MyMath::
使用类的另一个原因-使用访问说明符的选项。然后,您可以将公共静态方法分解为较小的私有方法。公共方法可以调用多个私有方法。
private
方法也比其原型根本不在标题中发布(因此仍然不可见)的方法更易于访问。我什至没有提到匿名命名空间函数提供的更好的封装。
.cpp
文件中未命名的名称空间中,这将使该信息对该翻译单元是私有的,而不会给阅读头文件的任何人任何多余的信息。实际上,我正在努力倡导PIMPL习惯用法。
.cpp
如果要使用模板,则不能将其放在文件中。
命名空间和类方法都有其用途。命名空间具有跨文件分布的能力,但是如果您需要将所有相关代码强制放入一个文件中,则这是一个弱点。如上所述,类还允许您在类中创建私有静态成员。您可以在实现文件的匿名名称空间中使用它,但是与将它们包含在类中相比,它的作用域仍然更大。
private:
。在许多情况下,似乎需要特权访问,可以将其排除在外。最“私有”的功能是没有出现在标题中的功能。private:
方法永远无法享受这种好处。