简体   繁体   中英

Angle class for radians and degrees

I want to do a angle class to be initialized in radians or degrees but i don't know if it is a good idea. I'm thinking about something like this:

类图

class Radian;
class Degree;

/**
  * My angle class.
  **/
class Angle
{
    private:
        float m_radian;

    public:
        explicit Angle(const Radian& rad);

        explicit Angle(const Degree& deg);

        float GetRadian(void) const
        {
            return this->m_radian;
        }

        float GetDegree(void) const
        {
            return ToDegree(this->m_radian);
        }

        bool IsEqual(const Angle& angle, float epsilon = 0.001f) const
        {
            return Abs<float>(this->m_radian - angle.m_radian) < epsilon;
        }

        void Set(const Angle& ang);

    protected:
        Angle(void) : m_radian(0.0f)
        {}

        Angle(float rad) : m_radian(rad)
        {}
};

class Radian : public Angle
{
    public:
        Radian(void)
        {}

        Radian(float r) : Angle(r)
        {}
};

class Degree : public Angle
{
    public:
        Degree(void)
        {}

        Degree(float d) : Angle(ToRadian(d))
        {}
};

/// Trigonometric functions.
float Sin(const Angle& angle);
...

This way i won't confuse radian and degrees in my code. But, is this a good idea or i should use other design?

You don't need any inheritance at all here. Once the objects are constructed, you don't care about the difference anymore - they behave exactly the same. So the only problem you've got to solve is how to make constructing Angle objects in a readable way.

The usual solution to this is to use named constructors :

class Angle
{
    public:
        static Angle fromRadians( float v );
        static Angle fromDegrees( float v );
        // ...

    private:
        Angle( float rad );
        // ...
};

Instead of invoking the constructor directly, you provide factory functions which expressive names. So you write:

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

I don't see the need for inheritance here. As far as using your class is concerned, all that matters is that you get an angle - whether it was in degrees or radians to start with is irrelevant.

Disclaimer: I've done this before. Exactly the same use case. My solution was to make the constructor take two arguments: a number and a unit enum. I'd use my class like so:

Angle a(1.2345, Angle::Radians);
std::cout << a.radians() << a.degrees() << sin(a);

If you want convenience methods to create angles from common units, they could be just that: helper methods. No need for separate classes.

Angle r = Radians(2.3);
Angle d = Degrees(180);

Anyway - just what I've been happy using in the past. Hope it helps!

I would suggest this:

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_;
}

This forces the natural units of a function to be part of its interface, which I consider to be a good thing. You should never write a Sin function that checks what kind of angle it was given, and does a different calculation. Either you write two versions, and let the compiler do the work:

float Sin(Radians x);
float Sin(Degrees x);

Or you just write one (using whatever type the implementation needs—probably radians):

float Sin(Radians x);

The point is not that you can have an "abstract" angle (which I don't believe is a useful concept), the point is to avoid ever confusing degrees with radians and to make the conversion between the two implicit.

Having a abstract base Angle class increases syntactic noise (you need to use references everywhere) and will probably decrease performance. This solution also allows you to store your angles in your desired units, instead of hoping that you're getting the "fast path."

我会使用角度作为对象和度和弧度作为相同内部角度值的不同吸气剂设定器。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM