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

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

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


当前回答

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

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

在面向对象代码中

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++的设计使得每个概念都是不同的,并且在不同的情况下,作为不同问题的解决方案被不同地使用。

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

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

其他回答

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


名称空间

优点:

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

缺点:

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


具有静态方法的类

优点:

can have private components (function, variables) and they are explicitly marked. classes can be friended can be type-parametrized (templates) can be template parameters themselves can be instantiated can be passed to functions (static functions behave like non-static method by default). it is easier to find patterns and go from groups of independent functions and convert them to a proper class (eventually with non static members) dependencies among classes is well defined functions (the static method) can be defined in any order

缺点:

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

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

像Bloomberg这样的公司更喜欢类而不是名称空间。 如果你不喜欢ADL或操作符重载,带静态方法的类是最好的选择。

在我看来,如果将命名空间和类集成成同一枚硬币的两面,那就太好了。 例如,如果方法默认是静态的,则将语言中的命名空间标识为类。 然后能够将它们作为模板参数使用。 我不确定用ADL做什么(可能它可以被限制为符号运算符函数,例如operatorX,这是运算符重载和ADL的最初动机)

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

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


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

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

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

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

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

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