I'm looking at implementing a suite of point types used in GIS, which are either 2D (xy), 3D (xyz or xym) or 4D (xyzm). The M-coordinate is a measure coordinate, and the others should be obvious. However, I can't seem to figure out how to make the PointZM
class share the x
and y
members from the Point
class. Here is my code:
#include <iostream>
class Point {
public:
double x, y;
Point (double xNew, double yNew) : x(xNew), y(yNew) {};
};
class PointZ : public Point {
public:
double z;
PointZ (double xNew, double yNew, double zNew) :
Point(xNew, yNew), z(zNew) {};
};
class PointM : public Point {
public:
double m;
PointM (double xNew, double yNew, double mNew) :
Point(xNew, yNew), m(mNew) {};
};
class PointZM : public PointZ, public PointM {
public:
PointZM (double xNew, double yNew, double zNew, double mNew) :
PointZ(xNew, yNew, zNew), PointM(xNew, yNew, mNew) {};
};
int main () {
Point p (1, 2);
PointZ pZ (1, 2, 3);
PointM pM (1, 2, 4);
PointZM pZM (1, 2, 3, 4);
std::cout << "Point: " << sizeof(p) << std::endl;
std::cout << "PointZ: " << sizeof(pZ) << std::endl;
std::cout << "PointM: " << sizeof(pM) << std::endl;
std::cout << "PointZM: " << sizeof(pZM) << std::endl;
}
Prints the size of the four instances of each class:
Point: 16
PointZ: 24
PointM: 24
PointZM: 48
I was expecting the last PointZM
to be 32 bytes, as it should have x
, y
, z
and m
members. How can I get the two inherited classes PointZ
and PointM
to share their inherited Point
members? Is the some way to get the union of two classes? I'm a C++ novice with this topic.
The motivation on why to bother with inheritance is not apparent with simple point geometries. However, when further developing different geometry types, like LineString
(2D), LineStringZ
(3D), LineStringM
(3D) or LineStringZM
(4D), they would have a length
method which would be different if there was a Z-dimension. The length method would be calculated differently only if there is a Z-dimension, and I wouldn't want to double the efforts to add this to LineStringZ
and LineStringZM
classes.
There is 2D geometry, 3D geometry and 4D geometry. 3D geometry is not a refined instance of 2D geometry, it is a totally different beast. Likewise 4D geometry is not a special case of 3D geometry.
Public inheritance is intended to express the is-a relationship. There's no such relationship between differently dimensioned geometries.
Non-public inheritance can be used for code sharing, but it is not clear what code can be shared between 2D, 3D and 4D cases.
However, all of those geometries are cases of generic N-dimensioned geometry, N being a parameter. It would be natural to model this with a class template.
template <size_t N>
class Point
{
std::array<double, N> coord;
// ... rest of the code ...
};
The direct answer to your question is to use virtual inheritance. This would make PointZM contain just one Point, rather than two. You might later decide this isn't worth the complexity, and instead just implement PointZ, PointM, and PointZM separately, but that's up to you.
On my system, virtual inheritance adds 8 bytes to each virtually-derived type, and PointZM stays the same size because the 16 bytes of the extra Point it contained are removed but we now have two extra pointers stored for the virtual base classes. So then the sizes look like this:
Point: 16
PointZ: 32
PointM: 32
PointZM: 48
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With