I'm reading through the chapter on inheritance from C++ Primer 5th Edition. It asks
Organize the following type into an inheritance hierarchy:
(b) Geometric primitives (such as box, circle, sphere, cone)
Identify some of the likely virtual functions as well as public and protected members.
I was hoping this is a suitable arrangement for the organisation of the hierarchy. My line of thinking was:
The most general property of any shape is its dimension. Thus the code could be factored in to having a base class at the top named
Shape
which would contain this piece of data.The necessary member functions will change depending on whether I have a 1D, 2D or 3D shape. 2D shapes will require an area and perimeter function, and 3D shapes a surfaceArea and volume function. The operations of these functions will vary depending on the shape. So define abstract base classes corresponding to each dimension with pure-virtual member functions.
Derive classes from the dimension classes corresponding to the final geometric shapes.
My main concern is that maybe the Shape
class isn't all that necessary if its only purpose is to hold dimension
and the static member pi
. A second concern is how to prevent creation of Shape
objects, since it only represents a concept and nothing tangible. I'm guessing to do this I would need to define the destructor as pure virtual - although I have not reached this stage of reading.
Shape.h:
#ifndef SHAPE
#define SHAPE
#include <cmath> //sqrt needed
class Shape{
public:
Shape() = default;
Shape(unsigned d): dimensions(d) { }
virtual ~Shape() = default;
private:
unsigned dimensions = 0;
protected:
static const double pi;
};
class DimensionOne : public Shape {
public:
DimensionOne(): Shape(1) { }
virtual double length() const = 0;
};
class DimensionTwo : public Shape {
public:
DimensionTwo(): Shape(2) { }
virtual double perimeter() const = 0;
virtual double area() const = 0;
};
class DimensionThree : public Shape {
public:
DimensionThree(): Shape(3) { }
virtual double surfaceArea() const = 0;
virtual double volume() const = 0;
};
//2D SHAPES
class Rectangle : public DimensionTwo {
public:
Rectangle() = default;
Rectangle(double a): DimensionTwo(), length(a), width(a) { }
Rectangle(double x, double y): DimensionTwo(), length(x), width(y) { }
double perimeter() const override { return 2*(length+width); }
double area() const override { return length*width;}
private:
double length = 0, width = 0;
};
class Circle : public DimensionTwo {
public:
Circle() = default;
Circle(double r): DimensionTwo(), radius(r) { }
double perimeter() const override { return 2*pi*radius; }
double area() const override { return pi*radius*radius;}
private:
double radius = 0;
};
//3D SHAPES
class Box : public DimensionThree {
public:
Box() = default;
Box(double a): DimensionThree(), length(a), width(a), height(a) { }
Box(double x, double y, double z): DimensionThree(), length(x), width(y), height(z) { }
double surfaceArea() const override { return (2*length*height) + (4*width*height); }
double volume() const override { return length*width*height;}
private:
double length = 0, width = 0, height = 0;
};
class Sphere : public DimensionThree {
public:
Sphere() = default;
Sphere(double r): DimensionThree(), radius(r) { }
double surfaceArea() const override { return (4*pi*radius*radius); }
double volume() const override { return (4*pi*radius*radius*radius)/3;}
private:
double radius = 0;
};
class Cone: public DimensionThree {
public:
Cone() = default;
Cone(double b, double h): DimensionThree(), base(b), height(h) { }
double surfaceArea() const override { return pi*base*(base + sqrt(base*base + height*height)); }
double volume() const override { return (pi*base*base*height)/3;}
private:
double base = 0, height = 0;
};
#endif // SHAPE
Shape.cpp:
#include "Shape.h"
const double Shape::pi = 3.14159;
draw()
method toShape
so that it has at least one common interface. \$\endgroup\$