C++ 弧度和度的角度类

C++ 弧度和度的角度类,c++,math,trigonometry,C++,Math,Trigonometry,我想做一个角度类初始化弧度或度,但我不知道这是一个好主意。我在想这样的事情: 类弧度; 班级学位; /** *我的天使课。 **/ 类角 { 私人: 浮动m_弧度; 公众: 显式角度(常数弧度和弧度); 显式角度(常数度和度数); 浮点数(无效)常数 { 返回此->m_弧度; } 浮点GetDegree(void)常量 { 返回到度数(此->m_弧度); } 布尔等质量(常数角和角度,浮点ε=0.001f)常数 { 返回Abs(这个->m_弧度-角度.m_弧度)

我想做一个角度类初始化弧度或度,但我不知道这是一个好主意。我在想这样的事情:

类弧度;
班级学位;
/**
*我的天使课。
**/
类角
{
私人:
浮动m_弧度;
公众:
显式角度(常数弧度和弧度);
显式角度(常数度和度数);
浮点数(无效)常数
{
返回此->m_弧度;
}
浮点GetDegree(void)常量
{
返回到度数(此->m_弧度);
}
布尔等质量(常数角和角度,浮点ε=0.001f)常数
{
返回Abs(这个->m_弧度-角度.m_弧度)

这样我就不会在代码中混淆弧度和度数。但是,这是一个好主意还是我应该使用其他设计?

对于相同的内部角度值,我会使用角度作为对象,使用度和弧度作为不同的getter setter。

我认为这里不需要继承。就使用你的类而言,重要的是你得到了一个角度——不管它是以度还是弧度开始,都是无关紧要的

免责声明:我以前做过。完全相同的用例。我的解决方案是让构造函数接受两个参数:一个数字和一个单位枚举。我会这样使用我的课堂:

Angle a(1.2345, Angle::Radians);
std::cout << a.radians() << a.degrees() << sin(a);
不管怎样,这正是我过去一直乐于使用的东西。希望有帮助

我建议:

class Radians {
    explicit Radians(float a) : angle_(a) {}
    Radians(Degrees a)        : angle_(a * PI/180.f) {}
    operator float()          { return angle_; }
private:
    float angle_;
}

class Degrees {
    explicit Degrees(float a) : angle_(a) {}
    Degrees(Radians a)        : angle_(a * 180.f/PI) {}
    operator float()          { return angle_; }
private:
    float angle_;
}
<>这迫使函数的自然单位成为它的接口的一部分,我认为这是一件好事。千万不要编写一个
Sin
函数来检查给定的角度,并进行不同的计算。要么编写两个版本,然后让编译器完成工作:

float Sin(Radians x);
float Sin(Degrees x);
或者您只需编写一个(使用实现需要的任何类型,可能是弧度):

重点不是你可以有一个“抽象”的角度(我不相信这是一个有用的概念),重点是避免永远混淆度和弧度,并使两者之间的转换隐式


拥有一个抽象基
Angle
类会增加语法噪音(您需要在任何地方使用引用),并且可能会降低性能。此解决方案还允许您以所需的单位存储角度,而不是希望获得“快速路径”。

您根本不需要任何继承。一旦构建了对象,您就不再关心它们之间的差异——它们的行为完全相同。因此,您必须解决的唯一问题是如何以可读的方式构造
角度
对象

通常的解决方法是使用:

不是直接调用构造函数,而是提供包含表达式名称的工厂函数。所以你写:

void f( Angle::fromDegrees( 3.0 ), Angle::fromRadians( 17.0 ) );

如果你有一个
角度
类,为什么还要有
弧度
?就个人而言,我认为这个三角形是一个有趣的模式,它以令人愉快的方式违反了OO原则。我第一次看到它是在埃菲尔铁塔上,用复数,从那以后我就爱上了它。@Beta我的Angle类是一个推广。所以,当我想使用度角时,我可以使用度(90)而不是角度(托拉迪安(90))。我想要一种不混淆弧度和度的方法。只要使用Angle,我就可以写出Angle(90)之类的错误。@Jerry Coffin我会检查一下,谢谢。我认为这样的想法是面向对象的想法在严肃的科学计算中不会产生影响的部分原因。我不认为单位转换是足够的行为差异,以保证两个新的类。这似乎是一个很好的解决方案。但是,在这种情况下,必须传递两个参数来初始化角度对象。如果你需要加载很多角度,那是不是很糟糕?在构造函数中,你有一个“如果”来处理angle kind?+1,继承用于修改行为,这里的行为没有任何区别来证明继承是正确的。@LucasNunes-你可以通过使
angle::UNIT
成为乘数(或乘数表的索引)来避免条件。所以,如果你在内部将角度存储为弧度,
angle:radians
将是
1.0
angle::Degrees
将是
0.0174532925
,等等。@LucasNunes-关于有两个参数:我假设这样的函数将由编译器内联,因为它非常小,在输入以弧度为单位的情况下,乘以1将得到优化。但如果你想确保弧度初始化速度更快,你有很多选择。您可以像Frerich建议的那样使用“命名构造函数”,或者使用friend helper方法,只使用一个(弧度)参数调用私有构造函数。在所有其他单位中,乘法是不可避免的。+1-完全正确。这里不需要两门课。没有一个做过科学计算的人会做这样的事情。+1“重点不是你可以有一个“抽象”的角度(我不相信这是一个有用的概念),重点是避免永远混淆度和弧度,并使两者之间的转换隐式。”-一针见血我认为隐式转换运算符在这种情况下有点危险。例如,
Radians{1}+Degrees{45}
可以很好地编译,但不会给出直观的结果。最好是重载必要的运算符,或者显式地进行转换(或者只提供一个
float Sin(Radians x);
float Sin(Degrees x);
float Sin(Radians x);
class Angle
{
    public:
        static Angle fromRadians( float v );
        static Angle fromDegrees( float v );
        // ...

    private:
        Angle( float rad );
        // ...
};
void f( Angle::fromDegrees( 3.0 ), Angle::fromRadians( 17.0 ) );