假设我有,或者将要写,一组相关的函数。假设它们与数学有关。在组织方面,我应该:
编写这些函数,并将它们放在我的MyMath命名空间中,并通过MyMath::XYZ()引用它们 创建一个名为MyMath的类,并使这些方法是静态的,并引用类似的MyMath::XYZ()
为什么我要选择其中一种来组织我的软件呢?
假设我有,或者将要写,一组相关的函数。假设它们与数学有关。在组织方面,我应该:
编写这些函数,并将它们放在我的MyMath命名空间中,并通过MyMath::XYZ()引用它们 创建一个名为MyMath的类,并使这些方法是静态的,并引用类似的MyMath::XYZ()
为什么我要选择其中一种来组织我的软件呢?
当前回答
为什么我要选择其中一种来组织我的软件呢?
如果您使用名称空间,您将经常遇到一个语言缺陷,即相互调用的函数必须以特定的顺序列出,因为c++无法在文件中看到进一步的定义。
如果使用类,则不会出现此缺陷。
在类中包装实现函数比维护所有实现函数的声明或以非自然的顺序放置实现函数以使其易于编译更简单。
其他回答
如果需要静态数据,请使用静态方法。 如果它们是模板函数,并且您希望能够为所有函数一起指定一组模板形参,那么可以在模板类中使用静态方法。
否则,请使用命名空间函数。
对于这些评论:是的,静态方法和静态数据往往被过度使用。这就是为什么我只提供了两个相关的场景,我认为它们会有所帮助。在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;
如果您不需要这样做,那么无论如何都要使用名称空间。
有很多人不同意我的观点,但我是这样看的:
类本质上是某种对象的定义。静态方法应该定义与该对象定义密切相关的操作。
如果你只是要有一组相关的函数,而不与底层对象或一种对象的定义相关联,那么我会说只使用名称空间。对我来说,从概念上讲,这更合理。
例如,在你的情况下,问自己,“MyMath是什么?”如果MyMath没有定义一种对象,那么我会说:不要让它成为类。
但就像我说的,我知道有很多人(甚至激烈地)不同意我的观点(特别是Java和c#开发人员)。
我更喜欢名称空间,这样可以在实现文件中的匿名名称空间中拥有私有数据(因此与私有成员相反,它根本不必显示在头文件中)。另一个好处是,通过使用命名空间,方法的客户端可以选择不指定MyMath::
你应该使用命名空间,因为命名空间比类有很多优点:
您不必在同一个头文件中定义所有内容 您不需要在头文件中公开所有实现 你不能使用类成员;可以使用名称空间成员 不能使用类,尽管使用名称空间通常不是一个好主意 使用类意味着在实际上没有对象时创建某个对象
在我看来,静态成员被过度使用了。在大多数情况下,它们并不是真正必要的。静态成员函数可能更适合作为文件作用域函数,而静态数据成员只是全局对象,只是有一个更好的、不应有的声誉。
默认情况下,使用命名空间函数。
类是用来构建对象的,而不是用来替换名称空间的。
在面向对象代码中
Scott Meyers在他的《Effective c++》一书中写了一篇关于这个主题的文章,“比起成员函数,更喜欢非成员非好友函数”。我在Herb Sutter的一篇文章中找到了关于这一原则的在线参考:http://www.gotw.ca/gotw/084.htm
需要知道的重要事情是:在c++中,与类在同一个命名空间中的函数,并且将该类作为参数,属于该类的接口(因为ADL在解析函数调用时会搜索这些函数)。
例如: 假设你有一个命名空间N 假设你有一个类C,在命名空间N中声明(换句话说,它的全名是N::C) 假设你有一个函数F,声明在命名空间N中(换句话说,它的全名是N::F) 假设函数F的参数中有一个C类型的参数 ... 那么N::F是N::C的公共接口的一部分。
命名空间函数,除非声明为“friend”,否则不能访问类的内部内容,而静态方法有权访问类的内部内容。
这意味着,例如,在维护类时,如果需要更改类的内部结构,则需要在其所有方法中搜索副作用,包括静态方法。
扩展我
向类的接口添加代码。
在c#中,即使没有权限,也可以向类中添加方法。但在c++中,这是不可能的。
但是,仍然在c++中,您仍然可以添加名称空间函数,甚至可以添加到别人为您编写的类中。
从另一方面来看,这在设计代码时很重要,因为通过将函数放在名称空间中,您将授权用户增加/完成类的接口。
扩展二世
前一点的一个副作用是,不可能在多个头文件中声明静态方法。每个方法必须在同一个类中声明。
对于名称空间,来自相同名称空间的函数可以在多个头文件中声明(几乎标准的swap函数就是最好的例子)。
第三扩展
命名空间的最酷之处在于,在一些代码中,你可以避免提到它,如果你使用关键字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++名称空间有自己的语义。
但它更进一步,因为您可以以类似于继承的方式组合名称空间。
例如,如果命名空间a具有函数AAA,命名空间B具有函数BBB,则可以声明命名空间C,并使用关键字using在该命名空间中引入AAA和BBB。
您甚至可以使用命名空间将一个命名空间的全部内容带入另一个命名空间,如命名空间D所示!
namespace A
{
void AAA();
void AAA2();
}
namespace B
{
void BBB();
}
namespace C
{
using A::AAA;
using B::BBB;
}
namespace D
{
using namespace A;
using namespace B;
}
void foo()
{
C::AAA();
// C::AAA2(); // ERROR, won't compile
C::BBB();
}
void bar()
{
D::AAA();
D::AAA2();
D::BBB();
}
结论
命名空间是命名空间的。 上课就是为了上课。
c++的设计使得每个概念都是不同的,并且在不同的情况下,作为不同问题的解决方案被不同地使用。
当需要名称空间时,不要使用类。
在本例中,您需要名称空间。