I have a very simple concrete class that represents a point in 2D space, it has two methods to shift the point given a radius and an angle: one modifies the object itself, the other creates another object leaving the first one unchanged:
template<class T =double> class cls_2DPoint
{
...
cls_2DPoint<T>& polar_move(const T r, const double a) noexcept
{
x+=r*std::cos(a); y+=r*std::sin(a); return *this;
}
cls_2DPoint<T> polar_moved(const T r, const double a) const noexcept
{
return cls_2DPoint<T>(x+r*std::cos(a), y+r*std::sin(a));
}
...
T x,y;
};
This is actually code that I'm using! While reviewing it I realized that this was a reckless design: not obvious, error prone, making the user code less readable. Since I'm struggling to find a convincing alternative, I thought to ask advice here: are there general conventions/guidelines for naming modifying and non-modifying methods? Are there best practices that could help me here to improve the design?
My suggestions.
namespace
instead of prefix for class namenamespace cls2D
{
class Point { ... };
}
Vector
namespace cls2D
{
class Point { ... };
class Vector { ... };
}
Define operator methods that work with the two object types. Prefer non-member functions if you can get away with it.
namespace cls2D
{
class Vector;
class Point
{
public:
Point& operator+=(Vector v);
Point& operator-=(Vector v);
private:
double x = 0;
double y = 0;
};
class Vector
{
public:
Vector& operator+=(Vector v);
Vector& operator-=(Vector v);
private:
double x = 0;
double y = 0;
};
Point from_polar(double r, double th);
Point from_vector(Vector v);
Vector from_point(Point p);
Point operator+(Point p, Vector v);
Point operator+(Vector v, Point p);
Vector operator+(Vector v1, Vector v2);
Vector operator-(Point p1, Point p2);
Vector operator-(Vector v1, Vector v2);
}
Please note that Point
and Vector
have the same member data. Hence, it's tempting to use just one class to represent both. However, I think of them as two different abstractions and deserve to be different classes.
I removed the template
part in my answer to make illustration of the idea simpler.
I assume you know how to add the necessary costructors to the classes.