Reputation: 1487
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?
Upvotes: 6
Views: 8067
Reputation: 36547
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."
Upvotes: 6
Reputation: 94549
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 ) );
Upvotes: 16
Reputation: 14619
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!
Upvotes: 11
Reputation: 19
I would use the angle as object and degree and radian as different getter setter for the same internal angle value.
Upvotes: 1