C++中的静态常量双精度数

11

这是使用静态常量变量的正确方式吗?在我的顶层类(Shape)中

#ifndef SHAPE_H
#define SHAPE_H

class Shape
{
public:

    static const double pi;
private:
    double originX;
    double originY;
};

const double Shape::pi = 3.14159265;

#endif

后来我在一个扩展Shape的类中使用了Shape::pi,但是却遇到了链接错误。我将const double Shape::pi = 3.14...移动到了Shape.cpp文件中,程序就可以编译了。为什么会出现这种情况呢?谢谢。

7个回答

13

如果您有一种方法将C++11(或更高版本)标志添加到编译器中,那么您就能够执行以下操作:

ifndef SHAPE_H
#define SHAPE_H

class Shape
{
public:

    static constexpr double pi = 3.14159265;
private:
    double originX;
    double originY;
};

#endif

C++11 开始,您可以将 const 表达式用于除整数类型以外的其他类型。这使您能够在声明和定义时直接使用常量变量。

更多详情请参见:https://en.cppreference.com/w/cpp/language/constexpr


我的回答已经有8年的历史了。那时候还没有C++17。不过你可以自由地编辑这个答案。 - Benjamin

12

静态的浮点数数据成员必须在源文件中定义和初始化。一个定义不能在头文件的class {}块之外,这是因为一个定义规则禁止这样做,并且只有整型数据成员可以在class {}块内初始化。

这也很不幸,因为作为代数值,如果手头上有即时值,对于优化来说是很好的,而不是从全局变量中加载。(虽然差异可能微不足道。)

不过,有一个解决方案!

class Shape
{
public:
    static double pi()
        { return 3.14159265; }

private:
    double originX;
    double originY;
};

class{}块内允许使用内联函数定义,包括静态函数。

此外,我建议使用M_PI来自于<math.h>的头文件,你也应该从<cmath>中获取。


此外,模板具有不同的链接方式,因此您可以使用模板使定义可见。 - justin
2
@Justin:我认为在这里使用“template”除了增加复杂性之外,没有任何好处。 - Potatoswatter
+1 对于这个解决方案(我会明确声明为“内联”)的内容。 - Andre Holzner
M_PI很方便,但它不是C++标准的一部分。 - quant_dev
@quant_dev 实际上,它是 POSIX 的一部分。但在实践中,许多非 POSIX 平台也会提供它。 - Potatoswatter
显示剩余2条评论

12
因为 const double Shape::pi = 3.14159265;Shape::pi的定义,而C++只允许一个符号的单一定义(称为one-definition-rule,缩写为ODR)。当该定义在头文件中时,每个翻译单位都会有自己的定义,这就违反了该规则。
将其移动到源文件中,你只能得到一个定义。

我有同样的问题。问题是,如果这是一个库,并且我希望用户在 .h 文件中看到 Shape::pi 的值,但不想让他们看到 cpp 内容(即其他函数定义),那我该怎么办? - Peter Lee

3

这发生的原因是您不能多次定义Shape::pi。当您在Shape.cpp中包含Shape.h时,它只被定义一次,然后在另一个cpp文件中每次使用Shape.h时再次定义。当您将程序链接在一起时,链接器会因为有多个定义而报错。


1

代码行 const double Shape::pi = 3.14159265; 应该在你的 Shape.cpp 文件中。头文件是用来声明变量的。一个变量只能被定义一次,因此必须在 .cpp 文件中进行。头文件说明如何使用这些变量和函数,而 cpp 文件说明要做什么。


0
实现一个函数,如果列表中存在该值,则返回其索引。否则,如果没有该值,则返回-1。如果列表中存在多个相同的值,则从底部删除第一个值。
public static intfindFromLast (List <Double> l, double value ) {///…}

0
对于原始数据类型(如int,double但不包括char []),您也可以在头文件的类定义中定义常量,例如:
class Shape
{
public:
    static const double pi = 3.14159265;

private:
    double originX;
    double originY;
};

这将允许更好的编译器优化。
编辑:如Dennis在下面指出的,这仅适用于整数类型,而不适用于double或float数据类型(但某些编译器会允许此操作)。

2
你只能对整数和枚举类型执行此操作。浮点类型不允许。 - Dennis Zickefoose
@Dennis:哎呀!你说得对!我被编译器愚弄了(gcc和icc允许在类内初始化静态常量double)... - Oliver

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接