This is a general question on programming style. Let's say I have an object Line which has some methods and private variables Point point_a_ and Point point_b_. Let's say that at some point I need to change the position of the two points. What programming style would you prefer between the following cases? They all do the same thing (or should do: I didn't compile, but seems pretty straightforward).
CASE 1
Class Line {
public:
Line(Point point_a, Point point_b) : point_a_(point_a), point_b_(point_b) {}
void UpdatePoints(Point point_a, Point point_b) {
point_a_ = point_a; point_b_ = point_b;
}
double Distance();
private:
Point point_a_;
Point point_b_;
};
int main (int argc, char * const argv[]) {
Point point_a(0,0,0);
Point point_b(1,1,1);
Line line(point_a,point_b);
std::cout<<line.Distance()<<"\n";
point_a.x = 1;
line.UpdatePoints(point_a,point_b);
std::cout<<line.Distance()<<"\n";
}
CASE 2
Class Line {
public:
Line(Point point_a, Point point_b) : point_a_(point_a), point_b_(point_b) {}
Point& point_a() { return point_a_; }
Point& point_b() { return point_b_; }
double Distance();
private:
Point point_a_;
Point point_b_;
};
int main (int argc, char * const argv[]) {
Point point_a(0,0,0);
Point point_b(1,1,1);
Line line(point_a,point_b);
std::cout<<line.Distance()<<"\n";
line.point_a().x = 1;
std::cout<<line.Distance()<<"\n";
}
CASE 3
Class Line {
public:
Line(Point* point_a, Point* point_b) : point_a_(point_a), point_b_(point_b) {}
double Distance();
private:
Point* point_a_;
Point* point_b_;
};
int main (int argc, char * const argv[]) {
Point point_a(0,0,0);
Point point_b(1,1,1);
Line line(&point_a,&point_b);
std::cout<<line.Distance()<<"\n";
point_a.x = 1;
std::cout<<line.Distance()<<"\n";
}
Any feedback is greatly appreciated!!
Thanks!
[EDIT] Speed is paramount in my software!
In this simple scenario I might just use public member variables.
Otherwise I would provide getters that return a const reference and matching setters.
class Line {
public:
Line(const Point& p1, const Point&p2) : m_p1(p1), m_p2(p2) {}
const Point& p1() const
{ return m_p1; }
const Point& p2() const
{ return m_p2; }
void setP1(const Point& p1)
{ m_p1 = p1; }
void setP2(const Point& p2)
{ m_p2 = p2; }
private:
Point m_p1;
Point m_p2;
};
Case three is totally out because it completely violates principles of encapsulation. Case two does as well, to a slightly lesser extent. I would prefer option one, but did you consider possibly making the points immutable and forcing you to create a new object when it changes?
Also to be pedantic if I remember correctly from many years ago, a line technically extends infinitely in both directions. You're actually representing a line segment.
Case 2 is no better than public member variables. In particular, it doesn't encapsulate anything.
Case 3 makes ownership of the points unclear. Consider what happens if your points are local variables in the calling function, and then they go out of scope. It also offers no benefits over public member variables.
So out of these three options, Case 1 is the cleanest, IMO. Other options are:
simply use public member variables.
make Line immutable.
use set and get functions.
I would choose either case 1 or an immutable Line class.
Case 2 allows changes to the Point objects without the knowledge of their containing line. At some point, you might need the line to know if its points have been changed.
Case 3 either makes the Line object dependent on the lifetime of the Points, or makes the Line the owner of the points, which is not clear from the API.
An immutable Line would allow you to create a new Line object with new points.
Case 4 -- Prefer passing by const reference rather than value (or pointer):
class Line
{
public:
Line(const Point& a, const Point& b) : a_(a), b_(b)
{}
const Point& get_a() const { return a_; }
const Point& get_b() const { return b_; }
void set_a(const Point& a) { a_ = a; }
void set_b(const Point& b) { b_ = b; }
private:
Point a_;
Point b_
};
This is better in that in enforces encapsulation - the only way to change the variables held in the class after construction is a specific mutator method.
The accessors return a const reference, so they cannot be modified (copies made from these can be).
The class as a whole is made const-correct.
References are arguably better than pointers in this instance because they are guaranteed (unless you specifically break that guarantee) to not be NULL.
Consider another option:
class Segment {
public:
Segment(Point point_a, Point point_b);
Point point_a() const;
Point point_b() const;
private:
Point point_a_;
Point point_b_;
};
double Distance( Segment seg );
int main (int argc, char * const argv[]) {
Point point_a(0, 0, 0);
Point point_b(1, 1 ,1);
Segment seg(point_a, point_b);
std::cout << Distance(seg) << "\n";
point_a.x = 1;
seg = Segment(point_a, point_b); // reset
std::cout << Distance(seg) << "\n";
}
I used the name Segment as per suggestion above. This style is closer to functional programming style. Segment is immutable unless you explicitly reset it with a common assignment syntax. Distance is not a member function because it can be implemented in terms of Segment's public interface.
Regards,
&rzej
Related
I have the simple data structure describing 2d point in cartesian coordinate system, like below.
struct CartPoint
{
double x;
double y;
}
and second strucutre, representing 2d point in polar coordinate system
struct PolarPoint
{
double r;
double alpha;
}
and also two functions allowing me to translate from one representation to second one:
void translate(const CartPoint& from, PolarPoint& to) { ... };
void translate(const PolarPoint& from, CartPoint& to) { ... };
I would like to create object (let me call it PointContainer), that allows me to store cartesian 2D points in one vector, but accessing those in either cartesian or polar representation (based on compile-time decision). I was thinking of a class exposing two types of non-const iterators, one for every representation. However I could not find anywhere such a solution and i am not sure whether it is a good idea. I would like to use it like that:
void fillVectorWithCartPts(std::vector<CartPoint>& points)
{
// fills points-vector with 2d cartesian points
...
};
int main()
{
std::vector<CartPoint> pts{};
fillVectorWithCartPts(pts);
PointContainer pc{pts};
// dummy logic representing use possibilities
for (CartPoint& _pt : pc.GetIterator<CartPoint>())
{
_pt = CartPoint{1.0, 2.0} // modifies points in data via cartesian representation
}
// or
for (PolarPoint& _pt : pc.GetIterator<PolarPoint>())
{
_pt = PolarPoint{3.0, 4.0} // modifies points in data via polar representation
}
// after modification i can retrive vector in selected representation
std::vector<PolarPoint> polarRes = pc.Retrive<PolarPoint>();
std::vector<CartPoint> cartRes = pc.Retrive<CartPoint>();
return 0;
}
I will be very grateful for any suggestion on design of such a class or proposing other solutions to solve the issue of double representation need for the same data.
It's likely the most idiomatic to create an intermediary class that can be viewed as either CartPoint or PolarPoint. You can do this multiple ways: containment and getters, or inheritance. Then you can store a vector of these points.
CartPoint toCartPoint(const PolarPoint& pp) { ... }
CartPoint toPolarPoint(const CartPoint& cp) { ... }
class GenericPoint {
public:
GenericPoint(CartPoint cp)
: cp(cp), pp(toPolarPoint(cp)) {}
GenericPoint(PolarPoint pp)
: cp(toCartPoint(pp)), pp(pp) {}
GenericPoint(const GenericPoint& gp)
: cp(gp.cp), pp(gp.pp) {}
operator const CartPoint&() const { return cp; }
operator const PolarPoint&() const { return pp; }
const CartPoint& getCartPoint() const { return cp; }
const PolarPoint& getPolarPoint() const { return pp; }
GenericPoint& operator=(const GenericPoint& rhs)
{
cp = rhs.cp;
pp = rhs.pp;
}
GenericPoint& operator=(const CartPoint& cp_in)
{
cp = cp_in;
pp = toPolarPoint(cp_in);
}
GenericPoint& operator=(const PolarPoint& pp_in)
{
cp = toCartPoint(pp_in);
pp = pp_in;
}
private:
CartPoint cp;
PolarPoint pp;
};
I suggest selecting one of them and only convert to the other when absolutely needed. If you select to store CartPoints internally, you could convert back and forth beween CartPoints and PolarPoints if PolarPoints are needed for some calculations. You'd store std::vector<CartPoint> in your container though.
Just keep in mind that floating point math is tricky and you may not get the same result if you do calculations in the PolarPoint domain and convert to CartPoint as if you did the calculations in the CartPoint domain.
It could nevertheless look something like this:
struct CartPoint {
double x = 0.;
double y = 0.;
CartPoint() = default;
CartPoint(double X, double Y) : x{X}, y{Y} {}
// convert a PolarPoint to a CartPoint
CartPoint(const PolarPoint& pp) :
CartPoint(pp.r * std::cos(pp.alpha),
pp.r * std::sin(pp.alpha))
{}
// comparisons
auto operator<=>(const CartPoint& rhs) const = default;
// return a Length proxy object in case the length is only used for comparisons
Length length() const { return {x*x + y*y}; }
// convert to a PolarPoint if needed by some function
operator PolarPoint () const {
return {std::sqrt(x*x+y*y), std::atan2(y, x)};
}
};
The Length proxy object is there to save us from calls to std::sqrt when we only want to compare the lengths of two CartPoints:
struct Length {
double len_squared;
// don't use sqrt if only comparing lengths
auto operator<=>(const Length& rhs) const {
return len_squared <=> rhs.len_squared;
}
// comparisons with actual doubles
auto operator<=>(double l) const {
return len_squared <=> l*l; // square the supplied value
}
// convert to double only when the actual length is needed
operator double() const { return std::sqrt(len_squared); }
};
Demo
I have a simple point class:
class Point {
public:
Point(const double, const double);
/** This constructor creates invalid point instance **/
Point();
~Point();
const double x;
const double y;
/** This returns true if one of the values is NaN **/
bool isInvalid() const;
/** Returns true if coordinates are equal **/
bool equal(const Point& p) const;
};
Values x and y are const so that I can be sure they never change. They are supposed to be always constant. The problem is I can't assign to variables holding Point:
Point somePoint;
... meanwhile, things happen ...
//ERROR: use of deleted function 'Point& Point::operator=(const Point&)'
somePoint = Point(x, y);
I understand that assigning is a problem because somePoint.x = something is forbidden. I need to use point to hold last point value during rendering:
Point lastPoint;
PointInGraph* point = graphValues.last;
while((point = point->next())!=nullptr) {
// calculate pixel positions for point
double x,y;
...
if(!lastPoint.isInvalid())
drawer.drawLine(round(lastPoint.x), round(lastPoint.y), round(x), round(y));
// ERROR: use of deleted function 'Point& Point::operator=(const Point&)'
lastPoint = Point(x, y);
}
So does const in class properties simply make any variable of that class type const as well? Or is there a workaround?
It's not possible. It would require modifying const values.
Instead of making x and y themselves const, make them non-const but provide a const interface to them, i.e. by making them private and providing const getters.
Instead of making the variables const you could just not provide any way for the user to change the values by:
making them private
only allowing assignment to a another instance as the only way to change the values.
You can see how this would work in the following example:
#include <iostream>
class Point {
public:
Point(const double x_ = 0, const double y_ = 0) : x(x_), y(y_) {}
double getX() const { return x; }
double getY() const { return y; }
private:
double x;
double y;
};
int main()
{
Point a{1,5};
Point p;
p = a;
std::cout << p.getX() << ", " << p.getY(); // no error here
//p.x = 5; // error here now
//p.y = 7; // error here now
}
Live Example
If you un-comment the last two lines you will get an error to prove that you cannot change x and y.
Actually it's, of course, possible by directly technique called cast away constness which is a known loophole in const mechanic in C++. Basically I can abuse the fact that const int* can be assigned to int* variable. The possible solution:
Point& Point::operator=(const Point& p)
{
*((double*)&x)=p.x;
*((double*)&y)=p.y;
}
This is of course not recommended, but I'm not the one of the people who think that knowledge is dangerous.
I would like to have an insight about whenever I should be using references or pointers.
Let's take the example of a Polygon class using a Rectangle class for its internal bounding box.
Polygon.h
class Polygon {
private:
std::list<Point> _points;
Rectangle _boundingBox;
public:
Polygon(const std::list<Point> &);
public:
const std::list<Point> &getPoints() const;
const Rectangle &getBoundingBox() const;
private:
void setBoundingBox();
};
Polygon.cpp
#include <iostream>
#include "Polygon.h"
Polygon::Polygon(const std::list<Point> &points)
{
if (points.size() < polygon::MIN_SIDE + 1) {
throw std::invalid_argument("A polygon is composed of at least 3 sides.");
}
if (points.front() != points.back()) {
throw std::invalid_argument("A polygon must be closed therefore the first point must be equal to the last one.");
}
std::list<Point>::const_iterator it;
for (it = ++points.begin(); it != points.end(); ++it) {
this->_points.push_back(*it);
}
this->setBoundingBox();
}
void Polygon::translate(const std::array<float, 2> &vector)
{
std::list<Point>::iterator it;
for (it = this->_points.begin(); it != this->_points.end(); ++it) {
(*it).setX((*it).getX() + vector[0]);
(*it).setY((*it).getY() + vector[1]);
}
Point topLeft = this->_boundingBox->getTopLeft();
Point bottomRight = this->_boundingBox->getBottomRight();
topLeft.setX(topLeft.getX() + vector[0]);
topLeft.setY(topLeft.getY() + vector[1]);
bottomRight.setX(bottomRight.getX() + vector[0]);
bottomRight.setY(bottomRight.getY() + vector[1]);
}
const std::list<Point> &Polygon::getPoints() const
{
return this->_points;
}
const Rectangle &Polygon::getBoundingBox() const
{
return this->_boundingBox;
}
void Polygon::setBoundingBox()
{
float xMin = this->_points.front().getX();
float xMax = this->_points.front().getX();
float yMin = this->_points.front().getY();
float yMax = this->_points.front().getY();
std::list<Point>::const_iterator it;
for (it = this->_points.begin(); it != this->_points.end(); ++it)
{
Point point = *it;
if (point.getX() < xMin) {
xMin = point.getX();
}
if (point.getX() > xMax) {
xMax = point.getX();
}
if (point.getY() < yMin) {
yMin = point.getY();
}
if (point.getY() > yMax) {
yMax = point.getY();
}
}
this->_boundingBox = new Rectangle(Point(xMin, yMin), Point(xMax, yMax));
}
std::ostream &operator<<(std::ostream &out, const Polygon &polygon)
{
std::list<Point>::const_iterator it;
for (it = polygon.getPoints().begin(); it != polygon.getPoints().end(); ++it) {
out << (*it);
if (it != polygon.getPoints().end()) {
out << " ";
}
}
return out;
}
Rectangle.h
#pragma once
#include <stdexcept>
#include "Point.h"
class Rectangle {
private:
Point _topLeft;
Point _bottomRight;
public:
Rectangle(const Point &, const Point &);
public:
const Point &getTopLeft() const;
const Point &getBottomRight() const;
float getWidth() const;
float getHeight() const;
};
Rectangle.cpp
#include "Rectangle.h"
Rectangle::Rectangle(const Point &topLeft, const Point &bottomRight)
{
if (topLeft.getX() > bottomRight.getX() || topLeft.getY() > bottomRight.getY()) {
throw std::invalid_argument("You must specify valid top-left/bottom-right points");
}
this->_topLeft = topLeft;
this->_bottomRight = bottomRight;
}
const Point &Rectangle::getTopLeft() const
{
return this->_topLeft;
}
const Point &Rectangle::getBottomRight() const
{
return this->_bottomRight;
}
float Rectangle::getWidth() const
{
return this->_bottomRight.getX() - this->_topLeft.getX();
}
float Rectangle::getHeight() const
{
return this->_bottomRight.getY() - this->_topLeft.getY();
}
Point.h
#pragma once
#include <ostream>
#include <cmath>
class Point {
private:
float _x;
float _y;
public:
Point(float = 0, float = 0);
public:
float distance(const Point &);
public:
float getX() const;
float getY() const;
void setX(float);
void setY(float);
};
std::ostream &operator<<(std::ostream &, const Point &);
bool operator==(const Point &, const Point &);
bool operator!=(const Point &, const Point &);
Point.cpp
#include "Point.h"
Point::Point(float x, float y)
{
this->_x = x;
this->_y = y;
}
float Point::distance(const Point &other)
{
return std::sqrt(std::pow(this->_x - other.getX(), 2) + std::pow(this->_y - other.getY(), 2));
}
float Point::getX() const
{
return this->_x;
}
float Point::getY() const
{
return this->_y;
}
void Point::setX(float x)
{
this->_x = x;
}
void Point::setY(float y)
{
this->_y = y;
}
std::ostream &operator<<(std::ostream &out, const Point &point)
{
out << "(" << point.getX() << ", " << point.getY() << ")";
return out;
}
bool operator==(const Point &p1, const Point &p2)
{
return p1.getX() == p2.getX() && p1.getY() == p2.getY();
}
bool operator!=(const Point &p1, const Point &p2)
{
return p1.getX() != p2.getX() || p1.getY() != p2.getY();
}
A lot of questions come with this snippet of code.
This does not compile because obviously whenever we try to create a Polygon, it ends up trying to create a Rectangle with a default constructor which does not exist.
I can't use initializer list because obviously the bounding box depends on some computed values from my list of points.
I could create a default constructor creating two Point(0, 0) by default for the Rectangle but this does not make much sense.
I could use pointers but then I feel this is not the nicest solution as I tend to think this is mostly used for polymorphism in C++ we should prefer reference whenever possible.
How should I then proceed ?
I feel I am missing something out about C++ and could learn a lot from this.
I think your main question is about how to deal with the problem of needing to initialize both std::list<Point> _points; and Rectangle _boundingBox;, while also doing some validation of _points.
The simplest solution is to just give Rectangle a default constructor (or pass two default Points as initializer). Then once you have validated the points argument in the constructor, you calculate the Rectangle based on the points.
A slightly more complicated alternative is to allow the validation function to be invoked from the ctor-initializer list, e.g.:
Polygon::Polygon(std::list<Point> points)
: _points( validate_point_list(points), std::move(points) ), _boundingBox( calculateBoundingBox(_points) )
{
}
where you have functions (which could be free functions):
void validate_point_list(std::list<Point> &points)
{
if (points.size() < polygon::MIN_SIDE + 1)
throw std::invalid_argument("A polygon is composed of at least 3 sides.");
if (points.front() != points.back())
throw std::invalid_argument("A polygon must be closed therefore the first point must be equal to the last one.");
// caller must pass in same first and last point, but we only store one of the two
points.erase( points.begin() );
}
and
Rectangle calculateBoundingBox(std::list<Point> const &_points)
{
// whatever logic you have in setBoundingBox, except return the answer
}
Note that the loop in your Polygon constructor is unnecessarily complicated. You could have just written _points = points; and then erased the extra point (which is O(1) for lists).
Note that I have passed by value and then used std::move. The reason is that if the argument given is a rvalue then it can just be moved right on through into where it's being stored; whereas with the const & version, a copy is stored and then the original is destructed.
I would use const & a lot less than you did. Small objects , such as Point and Rectangle, don't suffer a performance penalty from pass-by-value (and might even be more efficient that way). And as mentioned in the previous paragraph; if your function accepts a parameter and it is going to take a copy of that parameter, it's better to pass by value .
Passing by reference is best only when you are using but not storing the values passed. For example, calculateBoundingBox.
Finally, once you get this working, you might want to think about having the Polygon constructor accept an iterator pair of points range, and/or a std::initializer_list.
I would defined a default constructor for Rectangle class as private and I would make Polygon class a friend of Rectangle class:
class Rectangle {
friend class Polygon;
Point _topLeft;
Point _bottomRight;
Rectangle(); // accessible only to friends
public:
Rectangle(Point const&, Point const&);
...
};
And then in setBoundingBox():
void Polygon::setBoundingBox() {
...
_boundingBox._topLeft = Point(xMin, yMin);
_boundingBox._bottomRight = Point(xMax, yMax);
}
Thus, I wouldn't expose the default constructor of Rectangle and at the same time I would have a concrete object which is more efficient in terms of cache performance.
I feel as though you should have a separate class called BoundingBox that
1) Takes a collection of points in its constructor
2) Is inherited from Rectangle
Meanwhile, Rectangle should have a state, along the lines of NOT_A_RECTANGLE or it could throw an exception. Just be sure you clean up when throwing exceptions from a constructor.
Then you would construct the bounding box as part of the construction of the polygon and you can verify that a bounding box is possible as part of your error checking. (probably rather than 3 sides check, but I am no geometry expert)
BoundingBox would remain a member of Polygon.
This would be more RTTI.
It occurs to me though, that if you translate or rotate the polygon, you've also go to translate or rotate the bounding box. You might want to consider making the list of points its own object and sharing them. This would be a more advanced topic. You can for now get away with just recalculating the bounding box on operations performed upon the polygon.
As to whether to use a reference, a pointer, or pass by value, I don't know that there is a black and white list of things to consider for this, but a few are:
Is the object large enough to even worry about it? A rectangle is 4 floats?
Are there interfaces or base classes you will need to cast to, rather than always using the class itself? If so, you've got no choice but to use a pointer of some sort. The pointer could be unique, shared, weak, etc. depending on the situation. You have to ask yourself who owns it, whats the life time, and are there circular references?
Most people will probably use a reference whenever possible rather than a pointer, but only when passing by value doesn't qualify.
IMO, since you are just "GetBoundingBox", I think it would be simple and more maintainable to just return a copy of the bounding box by value rather than some const reference and definitely more than a pointer.
One solution would be to write a programmatic constructor for Rectangle that takes as its argument a const std::list<Point>&. It could traverse the list once, computing the maximum and minimum x and y. Then, your Polygon constructor would become:
Polygon::Polygon(const std::list<Point> &points)
: _points(points),
: _boundingBox(points)
{
// ...
}
An alternative is to move the code to find the bounding box from a list of points to a helper function, then define a move constructor Rectangle::Rectangle( Rectangle&& x ). In that case, your Polygon constructor would be:
Polygon::Polygon(const std::list<Point> &points)
: _points(points),
: _boundingBox( findBoundingBox(points) )
{
// ...
}
Either way, you could update a bounding box with assignment, so you might want an assignment operator like Rectangle& Rectangle::operator= ( Rectangle&& x ) to make that more efficient. You can skip the Rectangle&& versions if a Rectangle is just Plain Old Data. But if you do this a lot, you might overload Rectangle& findBoundingBox( const std::list<Point>& src, Rectangle& dest ) to update in place with no copying.
On a minor side note, I’d discourage you from using identifiers that begin with underscores, since those names are reserved in the global namespace in C++, and your libraries might declare something named _point.
I use two Points to define a Line and also a LineSegment, like:
class Point { ... };
class Line
{
Point p1, p2;
//...
};
class LineSegment
{
Point p1, p2;
//...
};
LineSegment has the same definition as Line, so I used typedef Line LineSegment at first instead of defining another LineSegment class. But soon, I found I couldn't define the function distance to calculate the distance between a point and a line or a point and a line segment.
class Point { ... };
class Line
{
Point p1, p2;
//...
};
typedef Line LineSegment;
double distance(const Point& a, const Line& b) { ... }
double distance(const Point& a, const LineSegment& b) { ... }
Certainly I will get a compile error.
So, my question is: Is there a better way to distinguish Line from LineSegment without redefine LineSegment?
I follow #Amadan's suggestion. Create a abstract class.
#include <iostream>
struct Point { int x, y; };
struct AbstractLine { Point p1, p2; };
struct Line : AbstractLine { };
struct LineSegment : AbstractLine { };
void distance(const Point& a, const Line& b)
{
std::cout << "void distance(const Point& a, const Line& b)" << std::endl;
}
void distance(const Point& a, const LineSegment& b)
{
std::cout << "void distance(const Point& a, const LineSegment& b)" << std::endl;
}
int main()
{
Point p;
Line a;
LineSegment b;
distance(p, a);
distance(p, b);
}
A line segment has a start point and an end point, whereas a line may be uniquely defined by only the point on it closest to the origin (or some other representation).
So the Line representation is either invalid or excessive.
Edit: Aha, I knew I was missing something! If you use the point on the line closest to the origin (x, y), then the slope is (–x/y), and a second point on the line can easily be constructed at (x+y, y–x). So the implementation of Line can compute that to delegate operations to LineSegment using a temporary object, at less expense than loading additional values from memory. (The temporary should live entirely in registers.)
class LineSegment {
std::array< Point, 2 > terminus;
public:
double angle() { return ... }
};
class Line {
Point nearest_origin;
LineSegment toLineSegment() {
return {
nearest_origin,
{ nearest_origin[0] + nearest_origin[1],
nearest_origin[1] - nearest_origin[0] }
};
}
public:
double angle()
{ return toLineSegment().angle(); }
};
(Ugh… now that I wrote all that I can see that actually you want delegation to go the other way here, and subtract the coordinates of LineSegment to produce a Line at the correct angle. Anyway, this works and you get the point. Such delegation can go both ways, whereas inheritance is usually a one-way street.)
Moreover, you shouldn't typedef things just because the data members inside are similar. Why not just typedef them both to arrays? A class represents a distinct concept.
I recommend against the AbstractLine suggestion, unless there are meaningful methods to put in there. Start from the interface and fill in the internal details in the most convenient way. Classes shouldn't share implementation just because they look the same, but because the implementations are doing conceptually the same thing.
Because math is tricky, math classes should be written to allow changing internal representations, and public inheritance is a bit dangerous. Because math doesn't involve "actors" and actions, but rather generic operations with properties like commutativity, OOP is often a poor fit in general.
Consider the following classes:
class Coord
{
public:
double _x, _y;
Coord(double x, double y)
{
_x = x;
_y = y;
}
};
class NamedPoint : public Coord
{
public:
int _id;
NamedPoint(int id, double x, double y) :
Coord(x,y),
_id(id)
{
}
};
I'd like to create a member function of NamedPoint -- coord() -- that returns a reference of type Coord corresponding to the NamedPoint.
For example, I'd like to something like:
const Coord& NamedPoint::coord()
{
return ((Coord)*this);
}
But I get a warning about temporary variables and I'm not crazy about it.
Of course, the following works:
Coord coord()
{
Coord c = *this;
return c;
}
But I'd rather return a reference.
Does anyone know if this is possible using inherited classes?
Sorry for not explaining the point of the function. I'm overloading the == operator differently for Coord and NamedPoint. Coord would simply check {x,y} and NamedPoint would check {id,x,y}. If I forget to cast a NamedPoint to a Coord before this == test, I'll use the wrong version.
So, while I realize that
(Coord)np1 == (Coord)np2
would give me what I want, I'd rather use something like
np1.coord() == np2.coord()
which I think is more clear as to what is going on.
What's the point of the function? NamedPoint is implicitly convertible to Coord anyway:
void foo(Coord& c)
{
c._x = 5;
}
NamedCoord nc(0, 1, 2);
foo(nc); // c references the Coord part of nc
Anyway, your function should simply use this conversion:
const Coord& NamedPoint::coord()
{
// Bad: takes the value of *this and slices off
// the derived bits, leaving a temporary Coord.
/* return ((Coord)*this); */
// Good: takes the value of *this and refers
// to the base bits, no temporaries.
return *this;
// (Same as:)
/* return ((Coord&)*this); */
}
#GMan gives the main solution.
However, it might be interesting to note in more detail the problem:
const Coord& NamedPoint::coord()
{
return ((Coord)*this);
}
This is much the same as:
const Coord& NamedPoint::coord()
{
Coord c = *this;
return c;
}
Here it is clear that you are returning a reference to a temporary on the stack, which makes the reference to it useless, and hence the warning.
Now in the case presented, Coord is the base class and hence we have the simple solution given by #Gman.
In the general case, the principle is that if you want a reference to something, you better make sure that something will still be around.