Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Polymorphism in C++ with shared inheritance

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.

like image 358
user3985527 Avatar asked Aug 28 '14 06:08

user3985527


2 Answers

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 ...
   };
like image 93
n. 1.8e9-where's-my-share m. Avatar answered Oct 01 '22 06:10

n. 1.8e9-where's-my-share m.


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
like image 39
John Zwinck Avatar answered Oct 01 '22 04:10

John Zwinck