更好地理解命名构造函数的习惯用法

我想让一个角度类以弧度或度数初始化,我想返回值而不是角度对象。我发现命名构造函数可能是最有效的方法,但我不能 100% 确定我将如何修改我的案例。

#pragma once
#define _USE_MATH_DEFINES
#include <cmath>
class Angle
{
public:
    static Angle toRadians(double value) 
    {
        return Angle((value * M_PI / 180.0f));
    
    }
    static Angle toDegrees(double value) 
    {
        return Angle(value / 180.0f * M_PI);
    }
private:
    double angle;
    Angle(double value) : angle(value) {};

};

std::cout << Angle::toRadians(19.48); // Should print 0.33999014

回答

你的Angle班级缺少一个不变式。也就是说,对于Angle类的任意对象,除了“它包含一个双精度”之外,没有什么可以说是真的。

考虑一下:如果我编写以下函数:

void do_something(Angle delta) {
  // do something
}

如果它可以包含度数或弧度,我应该如何使用 delta 并且我无法知道它是哪个?

信不信由你,这是您与构造函数斗争的根源。这是因为构造函数的主要工作是将对象置于其不变状态。没有不变量意味着没有参考点,所以不清楚构造函数应该做什么。

因此,让我们设置一个让我们do_something()自信地实现的不变量:“角度始终包含弧度”。您甚至可以使用“...并且该角度始终在[-pi, pi[范围内”来使用更严格的不变量。

很明显,您的toRadians()toDegrees()命名的构造函数在这种情况下毫无意义。事实上,他们从来没有真正做过,但现在很明显。我们想要的是从它们的参数中建立不变量的函数。fromRadians()并且fromDegrees()会更有意义。

class Angle {
public:
    static Angle fromRadians(double value) 
    {
        return Angle(value);    
    }
    
    static Angle fromDegrees(double value) 
    {
        return Angle(value / 180.0 * M_PI);
    }

    double asRadians() const {
      return radians;
    }

    double asDegrees() const {
      return radians / M_PI * 180.0;
    }

private:
  Angle(double value) : radians(value) {}
  double radians;
};

请注意,使用这样实现的类,使用Angle该类的代码甚至不必知道不变量。API 只是说:角度可以由度数或弧度构成,并且可以解释为度数或弧度。您将不变量设置为在实现类时指导您的标志。一旦构建了类,它们就不再有用了,直到您当然想要更新代码。

接下来,如果您希望能够轻松地将角度转储为人类可读的字符串,您可以实现以下operator<<(sts::ostream&, const Angle&)功能:

std::ostream& operator<<(std::ostream& stream, const Angle& angle) {
  stream << angle.asDegrees() << "deg";
  return stream;
}


以上是更好地理解命名构造函数的习惯用法的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>