关于c ++:命名空间+函数与类上的静态方法

Namespace + functions versus static methods on a class

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

  • 编写这些函数并将它们放在我的MyMath名称空间中,然后通过MyMath::XYZ()引用它们。
  • 创建一个名为MyMath的类,使这些方法成为静态的,并引用类似的MyMath::XYZ()
  • 为什么我会选择其中一个来组织我的软件?


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

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

    在面向对象的代码中

    Scott Meyers在这个主题上为他的有效C++书写了一个完整的条目,"更喜欢非成员非朋友函数,而不是成员函数"。我在Herb Sutter:http://www.gotw.ca/gotw/084.htm的一篇文章中找到了这一原则的在线参考。

    重要的是要知道:与类相同的命名空间中的C++函数属于该类的接口(因为在解析函数调用时ADL将搜索这些函数)。

    命名空间函数,除非声明为"friend",否则不能访问类的内部,而静态方法具有。

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

    扩展I

    向类的接口添加代码。

    在C中,您可以向类添加方法,即使您无权访问它。但是在C++中,这是不可能的。

    但是,在C++中,仍然可以添加命名空间函数,甚至是为某人编写的函数。

    另一方面,这在设计代码时很重要,因为通过将函数放在名称空间中,您将授权用户增加/完成类的接口。

    扩展二

    前一点的副作用是,不可能在多个头中声明静态方法。每个方法都必须声明在同一类中。

    对于名称空间,可以在多个头中声明来自同一名称空间的函数(几乎标准的swap函数就是最好的例子)。

    扩展三

    名称空间的基本酷性在于,在某些代码中,如果使用关键字"using",则可以避免提及它:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #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

    你甚至可以把"污染"限制在一个级别上:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #include <string>
    #include <vector>

    {
       using std::string ;
       string s ; // Ok
       vector v ; // COMPILATION ERROR
    }

    string ss ; // COMPILATION ERROR
    vector vv ; // COMPILATION ERROR

    为了正确使用几乎标准的交换习惯用法,必须使用这个"模式"。

    这对于类中的静态方法来说是不可能的。

    因此,C++命名空间有自己的语义。

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

    例如,如果您有一个带有函数a a a的命名空间A,一个带有函数b b b的命名空间B,那么您可以声明一个命名空间C,并使用关键字将aaa和bbb带到这个命名空间中。

    结论

    命名空间用于命名空间。上课是为了上课。

    C++被设计成每个概念是不同的,并且在不同的情况下被不同地使用,作为不同问题的解决方案。

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

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


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

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

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

    例如,在您的案例中,问自己,"什么是mymath?"如果MyMath没有定义一种对象,那么我会说:不要把它变成类。

    但正如我所说的,我知道有很多人会(甚至强烈地)反对我(尤其是Java和C开发人员)。


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

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

    对评论的回应是:是的,静态方法和静态数据往往被过度使用。这就是为什么我只提供了两个相关的场景,我认为它们会有帮助。在OP的特定示例(一组数学例程)中,如果他希望能够指定参数(例如,核心数据类型和输出精度),并将其应用于所有例程,他可能会做如下操作:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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;

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


    应该使用名称空间,因为名称空间比类具有许多优势:

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

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


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


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


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