类上的命名空间+函数与静态方法的比较

假设我有,或者将要写,一组相关的函数。假设它们与数学有关。在组织方面,我应该:

  1. 编写这些函数,并将它们放在我的MyMath命名空间中,并通过MyMath::XYZ()引用它们
  2. 创建一个名为MyMath的类,并使这些方法是静态的,并引用类似的MyMath::XYZ() . c

为什么我要选择其中一种来组织我的软件呢?

85316 次浏览
  • 如果需要静态数据,请使用静态方法。
  • 如果它们是模板函数,并且您希望能够为所有函数一起指定一组模板形参,那么可以在模板类中使用静态方法。

否则,请使用命名空间函数。


对于这些评论:是的,静态方法和静态数据往往被过度使用。这就是为什么我只提供了两个相关的场景,我认为它们可以有所帮助。在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#开发人员)。

我更喜欢名称空间,这样你就可以在实现文件中的匿名名称空间中拥有私有数据(因此它根本不必显示在头文件中,而不是private成员)。另一个好处是通过using你的命名空间,方法的客户端可以选择不指定MyMath::

你应该使用命名空间,因为命名空间比类有很多优点:

  • 您不必在同一个头文件中定义所有内容
  • 您不需要在头文件中公开所有实现
  • 你不能using类成员;你可以using命名空间成员
  • 你不能using class,尽管using namespace通常不是一个好主意
  • 使用类意味着在实际上没有对象时创建某个对象

在我看来,静态成员被过度使用了。在大多数情况下,它们并不是真正必要的。静态成员函数可能更适合作为文件作用域函数,而静态数据成员只是全局对象,只是有一个更好的、不应有的声誉。

默认情况下,使用命名空间函数。

类是用来构建对象的,而不是用来替换名称空间的。

在面向对象代码中

Scott Meyers在他的《Effective c++》一书中写了一整篇关于这个主题的文章,“比起成员函数,更喜欢非成员非好友函数”。我在Herb Sutter的一篇文章中找到了这个原则的在线参考:http://www.gotw.ca/gotw/084.htm

需要知道的重要事情是:在c++中,与类在同一个命名空间中的函数,并且将该类作为参数,属于该类的接口(因为诽谤联盟将在解析函数调用时搜索这些函数)。

例如:

  • 假设你有一个命名空间N
  • 假设你有一个类C,在命名空间N (换句话说,它的全名是N::C)中声明
  • 假设你有一个函数F,声明在命名空间N (换句话说,它的全名是N::F)中
  • 假设函数F的形参中有一个类型为C的形参

... 那么N: FC N::的公共接口的一部分。

命名空间函数,除非声明为“友元”;不能访问类的内部内容,而静态方法有权访问类的内部内容。

这意味着,例如,在维护类时,如果需要更改类的内部结构,则需要在其所有方法中搜索副作用,包括静态方法。

扩展我

向类的接口添加代码。

在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

这个“pattern"对于正确使用几乎标准的交换习惯用语是必需的。

这在类中的静态方法中是不可能做到的。

因此,c++名称空间有自己的语义。

但它更进一步,因为您可以以类似于继承的方式组合名称空间。

例如,如果你有一个命名空间A和一个函数AAA,一个命名空间B和一个函数BBB,你可以声明一个命名空间C,并在这个命名空间中使用关键字using引入AAABBB

你甚至可以用using namespace将一个命名空间的全部内容带入另一个命名空间,如命名空间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++的设计使得每个概念都是不同的,并且在不同的情况下,作为不同问题的解决方案被不同地使用。

当需要名称空间时,不要使用类。

在本例中,您需要名称空间。

使用类-选项的另一个原因是使用访问说明符。然后,您可以将公共静态方法分解为更小的私有方法。公共方法可以调用多个私有方法。

命名空间和类方法都有各自的用途。命名空间有能力分布在文件中,但如果你需要强制所有相关的代码进入一个文件,这是一个弱点。如上所述,类还允许您在类中创建私有静态成员。你可以把它放在实现文件的匿名命名空间中,但是它的作用域仍然比在类中更大。

我想总结并补充其他答案。此外,我的视角是只看头部的世界。


< h2 id = "名称" > < >强名称空间< /强> < / h2 >

优点:

  • 命名层次结构的简单解决方案
  • 它们没有语义,所以读起来更简单
  • 可以存在于不同的文件中(头文件)
  • 可以扩展
  • 诽谤联盟
  • 可以定义(using)。
  • 很好地与操作符过载
  • 可以用于品牌(你可以设计你的代码并在上面放置一个名称空间)

缺点:

  • 一切都是公开的
  • 私有的东西需要未命名的命名空间,所以它不是显式的
  • ADL(是的,有些人鄙视ADL)
  • 可以扩展(这可能是一件坏事,特别是与ADL结合使用时,扩展名称空间会改变现有代码的语义)
  • 函数需要按使用顺序定义(或声明)

带有静态方法的类

.

优点:

  • 可以有私有组件(函数,变量),并且显式地标记它们。
  • 课程可以加为好友
  • 可以被类型参数化(模板)
  • 模板参数本身可以吗
  • 可以实例化
  • 可以传递给函数(默认情况下,静态函数的行为与非静态方法类似)。
  • 从一组独立函数中找到模式并将它们转换为合适的类(最终具有非静态成员)会更容易一些。
  • 类之间的依赖关系定义得很好
  • 函数(静态方法)可以按任何顺序定义

缺点:

  • 没有诽谤联盟
  • 不能扩展
  • 到处都需要关键字static(取笑语言的机会)
  • 单独解决命名问题有点过头了。在这种情况下很难阅读。
  • 函数(静态方法)总是需要限定(myclassspace::fun)。没有办法声明快捷方式(using)。
  • 几乎无用的操作过载,需要复杂的朋友机制。
  • 不能用于打品牌。
  • 你需要记住以;结尾:)

总而言之,带有静态方法的类是更好的代码单元,允许更多的元编程,除了ADL和一些语法怪才之外,它们可以复制名称空间的所有功能,但有时它们可能会过度使用。

像Bloomberg这样的公司更喜欢类而不是名称空间。 如果你不喜欢ADL或操作符重载,使用静态方法的类是可行的 在我看来,如果将命名空间和类集成在一起,成为同一枚硬币的两面,那就太好了。 例如,如果方法默认是静态的,则将语言中的命名空间标识为类。 然后能够将它们作为模板参数使用。 我不确定ADL该做什么(可能它可以仅局限于符号运算符函数,例如operatorX,这是运算符重载和ADL的最初动机)

为什么我要选择其中一种来组织我的软件呢?

如果您使用名称空间,您将经常遇到一个语言缺陷,即相互调用的函数必须以特定的顺序列出,因为c++无法在文件中看到进一步的定义。

如果使用类,则不会出现此缺陷。

在类中包装实现函数比维护所有实现函数的声明或以非自然的顺序放置实现函数以使其易于编译更简单。