337

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

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

为什么我会选择其中一个来组织我的软件?

4

9 回答 9

270

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

类是构建对象,而不是替换命名空间。

在面向对象的代码中

Scott Meyers 为他的 Effective C++ 书写了一整篇关于这个主题的文章,“Prefer non-member non-friend functions to member functions”。我在 Herb Sutter 的一篇文章中找到了对这一原则的在线参考:http ://www.gotw.ca/gotw/084.htm

需要知道的重要一点是:在 C++ 中,与类在同一命名空间中并且以该类作为参数的函数属于该类的接口(因为ADL将在解析函数调用时搜索这些函数) .

例如:

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

...然后N::FN::C的公共接口的一部分。

命名空间函数,除非声明为“朋友”,否则无法访问类的内部,而静态方法可以。

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

扩展一

将代码添加到类的接口。

在 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++ 命名空间有自己的语义。

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

例如,如果您有一个A带有函数AAA的名称空间,一个B带有函数的名称空间BBB,您可以声明一个名称空间C,并使用关键字将和带入这个名称AAA空间。BBBusing

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

当你需要命名空间时不要使用类。

在您的情况下,您需要命名空间。

于 2009-09-16T19:51:06.410 回答
60

有很多人不同意我的观点,但我是这样看的:

类本质上是某种对象的定义。静态方法应该定义与该对象定义密切相关的操作。

如果您只是要拥有一组与底层对象或某种对象的定义无关的相关函数,那么我会说只使用命名空间。就我而言,从概念上讲,这要明智得多。

例如,在你的情况下,问问自己,“什么是 MyMath?” 如果MyMath没有定义一种对象,那么会说:不要让它成为一个类。

但就像我说的,我知道有很多人会(甚至强烈地)不同意我的观点(尤其是 Java 和 C# 开发人员)。

于 2009-09-16T19:25:33.140 回答
20
  • 如果您需要静态数据,请使用静态方法。
  • 如果它们是模板函数,并且您希望能够为所有函数一起指定一组模板参数,那么请在模板类中使用静态方法。

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


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

如果您不需要它,那么一定要使用命名空间。

于 2009-09-16T19:18:30.347 回答
12

您应该使用命名空间,因为命名空间比类具有许多优点:

  • 您不必在同一个标​​题中定义所有内容
  • 您不需要在标头中公开所有实现
  • 你不能using成为班级成员;你可以using是命名空间成员
  • 你不能using class,虽然using namespace这通常不是一个好主意
  • 使用类意味着当真的没有对象时要创建一些对象

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

于 2009-09-16T19:32:03.987 回答
4

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

于 2009-09-16T19:28:53.847 回答
3

我想总结并添加到其他答案。此外,我的观点是仅标题的世界。


命名空间

优点:

  • 命名层次结构的简单解决方案
  • 它们没有语义,因此更易于阅读
  • 可以存在于不同的文件(标题)中
  • 可以扩展
  • 日常活动
  • 可以定义快捷方式 ( using)。
  • 与运算符重载配合得很好
  • 可用于品牌化(您可以设计代码并在其上放置一个命名空间,但无需太多)

缺点:

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

具有静态方法的类

优点:

  • 可以有私有组件(函数、变量)并且它们被显式标记。
  • 班级可以交朋友
  • 可以类型参数化(模板)
  • 可以是模板参数本身
  • 可以实例化
  • 可以传递给函数(默认情况下,静态函数的行为类似于非静态方法)。
  • 更容易找到模式并从独立函数组中获取并将它们转换为适当的类(最终使用非静态成员)
  • 类之间的依赖关系定义明确
  • 函数(静态方法)可以按任何顺序定义

缺点:

  • 没有 ADL
  • 不能延长
  • 到处都需要关键字 static (取笑语言的机会)
  • 单独解决命名问题是一种矫枉过正的做法。在这种情况下很难阅读。
  • 函数(静态方法)总是需要限定(myclassspace::fun)。无法声明快捷方式 ( using)。
  • 对于运算符重载几乎没用,需要复杂的友元机制。
  • 不能用于品牌。
  • 你需要记住结束它; :)

总而言之,具有静态方法的类是更好的代码单元,并且允许更多元编程,并且除了 ADL 和一些语法怪癖之外,可以复制命名空间的所有功能,但有时它们可​​能有点过头了。

诸如 Bloomberg 之类的公司更喜欢类而不是名称空间。如果您不喜欢 ADL 或运算符重载,那么使用静态方法的类是不错的选择。

IMO,如果将命名空间和类集成为同一枚硬币的两个方面,那就太好了。例如,将语言中的命名空间标识为类,默认情况下方法是静态的。然后能够将它们用作模板参数。我不确定如何处理 ADL(可能它可能仅限于符号运算符函数,例如 operatorX,这是运算符重载和 ADL 的最初动机)

于 2021-03-28T09:48:26.080 回答
0

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

于 2011-11-13T07:41:54.620 回答
0

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

于 2015-04-16T03:42:41.730 回答
0

为什么我会选择其中一个来组织我的软件?

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

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

将实现函数包装在一个类中比维护所有实现函数的声明或以不自然的顺序使其编译更容易和更清晰。

于 2022-01-20T05:20:45.977 回答