I am writing a program that has different shape classes
There is a base shape class similar to the following:
class Shape
{
public:
Shape(int x, int y, int size, COLORREF colorRef);
~Shape();
bool operator == (const Shape&) const;
int x() const;
int y() const;
int size() const;
protected:
int xCoord;
int yCoord;
int shapeSize;
COLORREF color;
};
And then some derived classes similar to the following:
class Circle : public Shape
{
public:
Circle(int x, int y, int size, COLORREF colorRef) : Shape(x, y, size, colorRef)
{
this->radius = (double)shapeSize / 2;
this->xCenter = (double)xCoord + radius;
this->yCenter = (double)yCoord - radius;
}
~Circle() {}
private:
double radius;
double xCenter;
double yCenter;
};
class Square : public Shape
{
public:
Square(int x, int y, int size, COLORREF colorRef) : Shape(x, y, size, colorRef) {}
~Square() {}
};
class Triangle : public Shape
{
public:
Triangle(int x, int y, int size, COLORREF colorRef) : Shape(x, y, size, colorRef) {}
~Triangle() {}
};
I would like to overload the == operator in the shape class so that I can determine if 2 shapes are identical. If I could assume both shapes being compared were of the same class then I know it would be fairly straight forward, but how do I go about testing whether 2 objects of the different derived classes are equal? For example, how do I determine that Triangle t != Circle c?
You have to determine which function to call based on type of two objects. This pattern in C++ is called double-dispatch (or Visitor pattern).
The most common implementation assumes that all derived classes (shapes in your example) are known - so you can list them in base class:
class Circle;
class Rectangle;
// all shapes here
class Shape {
public:
virtual ~Shape() = default; // good habit is to add virtual destructor to all polymorphic classes (those with virtual methods)
bool operator == (const Shape& other) const {
return equalTo(other);
}
virtual bool equalTo(const Shape& other) const = 0;
virtual bool doEqualTo(const Circle& other) const { return false; }
virtual bool doEqualTo(const Rectangle& other) const { return false; }
// etc.. for all other shapes
};
class Circle : public Shape {
// ...
protected:
virtual bool equalTo(const Shape& other) const
{
return other.doEqualTo(*this); // call doEqualTo(Circle) - first virtual dispatch
}
virtual bool doEqualTo(const Circle& other) const
{
return other.center == center && other.radius == radius; // second virtual dispatch
}
};
As you can see - to perform action - you have to call 2 virtual functions (so double-dispatch)
Ok, here's an idea for using the curious recurring template pattern to make implementing derived classes easier while allowing the == operator to work as expected. This maybe overkill, but it should work for your scenario.
Start by filling out your base Shape class. Added to your basic definition is an implementation of operator== that invokes a helper called CompareTypesAndDimensions. The function calls into two virtual methods, TypeCompare and Compare.
class Shape
{
public:
Shape(int x, int y, int size, COLORREF colorRef) : xCoord(x), yCoord(y), shapeSize(size), color(colorRef) {}
virtual ~Shape() {}; // need at least one virtual member for dynamic_cast
int x() const { return xCoord; }
int y() const { return yCoord; }
int size() const { return shapeSize; }
COLORREF col() const { return color; };
bool operator == (const Shape& other) const
{
return CompareTypesAndDimensions(other);
}
bool BaseShapeCompare(const Shape& other) const
{
return ((other.xCoord == xCoord) && (other.yCoord == yCoord) && (other.shapeSize == shapeSize) && (other.color == color));
}
virtual bool TypeCompare(const Shape& other) const = 0;
virtual bool Compare(const Shape& other) const = 0;
bool CompareTypesAndDimensions(const Shape& other) const
{
// make sure the types checks are reciprocals
// we don't accidently compare a "Square" with a "Rectangle" if they inherit from each other
if (TypeCompare(other))
{
return Compare(other);
}
return false;
}
protected:
int xCoord;
int yCoord;
int shapeSize;
COLORREF color;
};
The idea being with the above is that Circle, Triangle, and Square could just implement their own version of TypeCompare and Compare and be done with it. But wait! What if we could save some typing by having a template base class do some work for us - especially for validating that both compared instances are of the same type. And not having to a stock Compare function for the simpler types such as Square and Triangle.
Let's introduce a template class that inherits from Shape. This class, ShapeComparable provides the implementations for Compare and TypeCompare. The only thing it needs the concrete class below it to deal with is a method to handle comparing its own methods.
template <typename T>
class ShapeComparable : public Shape
{
public:
ShapeComparable(int x, int y, int size, COLORREF colorRef) : Shape(x, y,size,colorRef)
{}
bool TypeCompare(const Shape& other) const override
{
auto pOtherCastToDerived = dynamic_cast<const T*>(&other);
return (pOtherCastToDerived != nullptr);
}
bool Compare(const Shape& other) const override
{
if (BaseShapeCompare(other))
{
auto pOtherCastToDerived = dynamic_cast<const T*>(&other);
if (pOtherCastToDerived)
{
return this->CompareDerived(*pOtherCastToDerived);
}
}
return false;
}
// derived classes that don't have members to compare will just inherit this member
virtual bool CompareDerived(const T& other) const
{
return true;
}
};
The magic with the above is that TypeCompare utilizes a dynamic_cast to validate if the two instances being compared are of the same type. If you try to compare a Triangle to a Circle, the dynamic cast fails. Hence, operator== will return false.
Now let's see what the rest of the classes look like. Start with Circle, it inherits from ShapeComparable and provides an implementation for CompareDerived.
class Circle : public ShapeComparable<Circle>
{
public:
Circle(int x, int y, int size, COLORREF colorRef) : ShapeComparable(x,y,size,colorRef)
{
this->radius = (double)shapeSize / 2;
this->xCenter = (double)xCoord + radius;
this->yCenter = (double)yCoord - radius;
}
bool CompareDerived(const Circle& other) const
{
// BaseCompare has already been invoked by the time this method is invoked.
return ((other.radius == radius) && (other.xCenter == xCenter) && (other.yCenter == yCenter));
}
private:
double radius;
double xCenter;
double yCenter;
};
But Triangle and Square are as simple as it gets.
class Triangle : public ShapeComparable<Triangle>
{
public:
Triangle(int x, int y, int size, COLORREF colorRef) : ShapeComparable(x, y, size, colorRef) {}
};
class Square : public ShapeComparable<Square>
{
Square(int x, int y, int size, COLORREF colorRef) : ShapeComparable(x, y, size, colorRef) {}
};
And if you ever need to introduce a new property to Triangle and Square, you just need to provide a CompareDerived method.
The above works with the assumption is that you wouldn't have additional shapes derived from another concrete shape class. Otherwise, the CompareType function won't be reciprocal when comparing a Square to a Rhombus.
Related
I have been given this task to do :Task Description
Currently, I have no clue on how to do it. Any help will be appreciated. There is soo many classes therefore all cannot be shown.
If you are able to show me how to do it would be much appreciated. If not, any resources where I can learn to be able to do it would be really helpful too. I am currently struggling on this section.
The Scene class:
Scene::Scene() {
std::string blankline(WIDTH, ' ');
for (int i = 0; i < HEIGHT; i++)
{
page_ += (blankline + "\n");
}
}
void Scene::addObject(std::shared_ptr<Shape> ptr) {
vectPage_.push_back(ptr);
}
void Scene::setDrawDepth(int depth) {
sceneDepth_ = depth;
}
std::ostream& operator<<(std::ostream& out, const Scene& s)
{
return out;
}
Stuff in constructor was done myself
Scene.h
class Scene {
public:
// Constructor
Scene();
// Add the pointer to the collection of pointers stored
void addObject(std::shared_ptr<Shape> ptr);
// Set the drawing depth to d
void setDrawDepth(int d);
// Constants specifying the size of the drawing area
static constexpr int WIDTH = 60;
static constexpr int HEIGHT = 20;
private:
std::string page_;
std::vector<std::shared_ptr<Shape>> vectPage_;
int sceneDepth_;
friend std::ostream& operator<<(std::ostream& out, const Scene& s);
};
Base Class.h
class Shape
{
public:
// Constructor specifying the depth of the object.
// If d is negative, throw a std::invalid_argument exception.
Shape(int d);
// Set depth of object to d. If d is negative, return false and
// do not update depth. Otherwise return true
virtual bool setDepth(int d);
// Return the depth of object
virtual int getDepth() const;
// Return the dimension of the object (0, 1 or 2)
virtual int dim() const = 0;
// Translate the object horizontally by x and vertically by y
virtual void translate(float x, float y) = 0;
// Rotate the object 90 degrees around its centre
virtual void rotate() = 0;
// Scale the object by a factor f relative to its centre.
// If f is zero or negative, throw a std::invalid-argument exception.
virtual void scale(float f) = 0;
// Return true if the object contains p and false otherwise.
// Depths are ignored for purpose of comparison
virtual bool contains(const Point &p) const = 0;
// the constant pi
static constexpr double PI = 3.1415926;
virtual ~Shape() = 0;
protected:
int d;
float getX() const;
float getY() const;
};
I'm trying to make a Chess game, and I'm having difficulties with creating the objects. My thought process went something like this:
Create a game board with 64 tiles, the tiles would have their own position and a pointer to a piece, thus I would be able to "easily" load in and unload a piece.
Tile class:
class Tile {
private:
int x;
int y;
Piece* piece;
public:
Tile(int x, int y) {
this->x = x;
this->y = y;
piece = nullptr;
}
void loadTile(Piece piece) {
this->piece = new Piece(piece); //this gives an error
}
void unloadTile() {
delete this->piece;
}
};
The individual pieces would then inherit from a parent class (below you can see a dumbed-down version). Inherited pieces would all have the same constructor and they would only differ in the way they calculate the possible moves. This, in my mind, is the best scenario to use a virtual function.
Piece and pawn class:
class Piece {
protected:
int x;
int y;
public:
Piece(int x, int y) {
this->x = x;
this->y = y;
}
virtual vector<int> returnPossibleMoves() = 0;
};
class Pawn : public Piece {
public:
using Piece::Piece;
vector<int> returnPossibleMoves() {
vector<int> moves;
moves.push_back(10); //dont think about this too much
return moves;
}
};
And here is the problem - the loadTile() function cannot instantiate the piece object because it is abstract.
I can see that my code may not work because I try to instantiate Piece with Pawn, but I don't really know how I would make it work, or what the workaround for this is. Hopefully you will be able to see what I'm trying to go for.
To strictly answer the question: you cannot create instances of abstract classes. That's why new Piece is not allowed. You would have to create an instance of a derived type that is not abstract, such as Pawn, and assign the piece pointer to point to that:
void Tile::loadTile() {
this->piece = new Pawn; //this is allowed
}
There are clearly some design changes that you'll need to make with this in mind, some of which have been mentioned in the comments on your question.
The Tile don't know which Piece type to instantiate, this is the fundamental problem.
What about something like this? (Disclaim, I just implemented some ideas, the code need probably lot of improvements until to get to sufficient quality)
#include <array>
#include <cassert>
#include <memory>
#include <vector>
using namespace std;
class Piece;
using CPiecePtr = std::shared_ptr<const Piece>;
enum class PieceType
{
Pawn
};
class Pos
{
int m_x=0;
int m_y=0;
public:
Pos()=default;
Pos(const Pos&)=default;
Pos& operator=(const Pos&)=default;
Pos( int x, int y): m_x(x), m_y(y)
{
assert(x>=0 && x<8 && y>=0 && y<8);
}
int x() const { return m_x; }
int y() const { return m_y; }
};
class Move
{
Pos m_origin;
Pos m_destination;
public:
Move()=default;
Move(const Move&)=default;
Move& operator=(const Move&)=default;
Move( const Pos& orig, const Pos& dest): m_origin(orig), m_destination(dest){}
const Pos& getDestination() const { return m_destination; }
const Pos& getOrigin() const { return m_origin; }
};
using MoveSet = std::vector<Move>;
class Tile
{
private:
CPiecePtr m_piece;
public:
void loadTile(CPiecePtr piece)
{
m_piece = piece;
}
void unloadTile()
{
m_piece = nullptr;
}
void setPiece(CPiecePtr piece) // this is more generic than previous two functions
{
m_piece = piece;
}
CPiecePtr getPiece() const
{
return m_piece;
}
};
class Piece
{
PieceType m_type;
public:
virtual MoveSet returnPossibleMoves(const Pos&) const = 0;
Piece(): m_type(PieceType::Pawn){}
PieceType getType() const { return m_type; }
};
class Pawn : public Piece
{
public:
MoveSet returnPossibleMoves(const Pos& pos) const override
{
MoveSet moves;
moves.push_back(Move(pos, Pos(pos.x(), pos.y()+1)));
//...
//TODO how to manage special moves? King-rook, replace pawn at end line...
return moves;
}
};
class Chess
{
private:
std::array<std::array<Tile,8>,8> m_board;
std::vector<CPiecePtr> m_pieces;
public:
Chess()
{
m_pieces.push_back( std::make_shared<const Pawn>());
//...
setPieceAt(Pos(0,1), m_pieces[0]);
}
CPiecePtr getPieceAt( const Pos& pos) const
{
return m_board[pos.x()][pos.y()].getPiece();
}
void setPieceAt( const Pos& pos, CPiecePtr piece)
{
return m_board[pos.x()][pos.y()].setPiece(piece);
}
// example:
MoveSet getMoveSetForPos( const Pos& pos)
{
const auto& piecePtr = getPieceAt(pos);
if (nullptr != piecePtr)
{
return piecePtr->returnPossibleMoves(pos);
}
return {};
}
void movePiece( const Move& move)
{
const auto& prevPiece = getPieceAt(move.getOrigin());
const auto& nextPiece = getPieceAt(move.getDestination());
assert(prevPiece && !nextPiece);
setPieceAt(move.getDestination(), prevPiece);
setPieceAt(move.getOrigin(), nullptr);
}
};
int main()
{
Chess chess;
const auto& moves = chess.getMoveSetForPos(Pos(0,1));
if (moves.size()>0)
{
chess.movePiece(moves[0]);
}
assert( chess.getPieceAt(Pos(0,2))->getType() == PieceType::Pawn);
return 0;
}
EDIT: I was not very proud of the answer, so I edited the code to make it compile. However, a fully working Chess is more complex than that, I leave how to manage king-rook and other special moves to the reader.
I'd like to avoid object slicing by using dynamic_cast. I'm trying to use CRTP to avoid writing assignment operator for every derived class. The base class is "Shape" and there are several derived classes("Circle" is an example). The purpose is to just use other Shape class as a template without writing assignment operator for each of them, like this class squre: public ShapeCopyable<square> However, the compiler complains at the line of return *this; saying:
error C2440: 'return': cannot convert from 'ShapeCopyable' to 'Circle &'
But it looks ok to me because the inheritance is this: Shape->ShapeCopable->Circle. I should be able to return an object of ShapeCopyable to a reference of Circle because they are from the same inheritance hierarchy, right? Where's the error? How should I fix it?
BTW, the vector of Shape* is the holder of all kinds of Shape pointers and the pointers held by it will be distributed to their corresponding Shape(square, circle, etc) vectors later on.
The code is enclosed below.
class Shape {
protected:
string name;
int edges;
virtual void assign(const Shape &rhs) {
name = rhs.name;
edges = rhs.edges;
}
};
template<typename T>
class ShapeCopyable : public Shape
{
public:
T & operator=(const Shape& s)
{
T const& c = dynamic_cast<T const&>(s); // Throws on bad cast.
assign(c);
return *this; //The compiler complains at this line
}
};
class Circle: public ShapeCopyable<Circle> {
private:
int radius;
public:
// preferably, this operator= is not needed.
Circle & operator=(Shape const &rhs) {
ShapeCopyable<Circle>::operator=(rhs);
return *this;
}
Circle(int in = 0) :radius(in) {}
std::string getName() { return name; }
int getEdges() { return edges; }
int getRadius() { return radius; }
void setRadius(int r) { radius = r; }
protected:
void assign(const Circle & rhs) {
Shape::assign(rhs);
radius = rhs.radius;
}
};
main()
{
std::vector<Shape*> shapes;
std::vector<Circle*> circs;
Circle c2(5); //Creates a circle with 5 for the radius.
shapes.push_back(&c2); //Pushing the 5-radius circle into the Shapes* vector
Circle c3; //Creates a circle with default constructor (which does NOT define radius)
c3 = *shapes[0]; //Now, the overloaded assignment operator. Look at Circle::assign(const Shape&) function
circs.push_back(&c3); //We push our newly assigned circle to our Circle vector
std::cout << "c3 radius: " << circs[0]->getRadius(); //This will be 5!
}
Change the line:
return *this; //The compiler complains at this line
to this:
return dynamic_cast<T&>(*this);
The reason why the compiler complained about it is because you are trying to return a ShapeCopyable when the compiler is expecting a reference to a concrete shape like Circle or Square.
There is no implicit conversion from a reference to a base class to a reference to a derived class, which necessitates a cast.
Ok, I'm answering my own question... After the following fix, the code worked as I expected.
template<typename T>
class ShapeCopyable : public Shape
{
public:
T & operator=(const Shape& s)
{
T const& c = dynamic_cast<T const&>(s); // Throws on bad cast.
static_cast<T*>(this)->assign(c); //this line got fixed
return dynamic_cast<T&>(*this); //this line got fixed
}
};
class Circle: public ShapeCopyable<Circle> {
private:
int radius;
public:
using Shapecopyable<Circle>::operator=; //this line got fixed
Circle(int in = 0) :radius(in) {}
std::string getName() { return name; }
int getEdges() { return edges; }
int getRadius() { return radius; }
void setRadius(int r) { radius = r; }
//The rest code is the same as before
...
...
}
My Question is the following, let's say I have this classes:
class ID3OBJ
{
public:
const double X;
const double Y;
const double Z;
ID3OBJ(double x, double y, double z);
ID3OBJ();
const ID3OBJ operator+ (const ID3OBJ param) const;
}
class Vector : public ID3OBJ
{
public:
Vector(double x, double y, double z);
Vector();
/*const Vector const operator+ (const Vector param); */
}
class Point : public ID3OBJ
{
public:
Point(double x, double y, double z);
Point();
}
class Plane : public ID3OBJ
{
public:
Point Origin;
Vector Direction1;
Vector Direction2;
Vector NormalVector;
Plane(Point Origin, Vector Normalvector);
Plane(Point Origin, Vector Direction1, Vector Direction2);
Plane();
}
class D3GON : public ID3OBJ, public Plane
{
public:
std::vector<Point> EdgePoints;
D3GON(Point P1, Point P2, Point P3);
};
In my current code I have to redefine the operator overload for each class, how can I avoid this code-duplication?
Do I have to add convert-functions?
I use const member-values, to deny objects changes after creation. Meaning if any low-level object has to be changed it has to be replaced by a new one. See my operator overload below:
// operator overwrites:
const ID3OBJ ID3OBJ::operator+ (const ID3OBJ param) const { double newX, newY, newZ; newX = X + param.X; newY = Y + param.Y; newZ = Z + param.Z; return ID3OBJ(newX, newY, newZ); }
Thank you :)
The Curiously recurring template pattern is the way to go here. It is slightly more complex because you want multiple derivation levels. But here is a code example:
template <class T>
class ID3OBJ
{
public:
double X,Y,Z;
T operator+(const T& obj) const {
T t;
t.X = X + obj.X;
t.Y=Y+obj.Y;
t.Z=z+obj.Z;
return t;
}
};
class Vector : public ID3OBJ<Vector>
{
public:
// some stuff
};
class Point : public Vector, public ID3OBJ<Point>
{
public:
// X, Y and Z exist twice, once in Vector, once in ID3OBJ<Point>, so we must disambiguate
using ID3OBJ<Point>::X;
using ID3OBJ<Point>::Y;
using ID3OBJ<Point>::Z;
};
You can add Vector (and you'll get Vector) or just Point and you'll get Point. More interestingly, if you add Point and Vector, you get an Vector result because the overloaded operator + resolution will select Vector::operator +
I need to be able to use a base pointer to hold the address of either a
rectangle or a circle. Which one would be determined at run time. I then want to
use the pointers to call different virtual functions depending on which type
they are. I can get this concept to work if the functions only use one Shape
pointer. However many of my functions require two objects to work.
If I use a pure virtual functions, both Rectangle and Circle classes become
abstract and I can not use the objects (error C2259). If I declare the functions
as I did down below, all of the calls go to the Base class Shape. Any help is
greatly appreciated.
class Shape {
public:
virtual double overlappingArea(const Shape&)const {return 0;};
//replacing with a pure virtual function causes the other classes to become abstract
//virtual double overlappingArea(const Shape&)const = 0;
//This returns error C2259 (or pure virtual function has no overload)
//I know this is because the program has no overloads with identical parameters
};
class Rectangle : virtual public Shape {
public:
Rectangle(int X, int Y, int L, int W) : x(X), y(Y), l(L), w(W) {}
double overlappingArea(const Rectangle& R)const {
double area = 1.1;
//code that finds the overlapping area
return area;
}
double overlappingArea(const Circle& C)const {
double area = 1.2;
//code that finds the overlapping area
return area;
}
private:
int x, y, l, w;
};
class Circle: virtual public Shape {
public:
Circle(int X, int Y, int R) : x(X), y(Y), r(R) {}
double overlappingArea(const Rectangle& R)const {
double area = 2.1;
//code that finds the overlapping area
return area;
}
double overlappingArea(const Circle& C)const {
double area = 2.2;
//code that finds the overlapping area
return area;
}
private:
int x, y, r;
};
int main() {
Shape* F1 = new Rectangle(0,0,1,1);
Shape* F2 = new Rectangle(1,1,2,2);
Shape* C1 = new Circle(0,0,1);
Shape* C2 = new Circle(1,1,2);
double areaFF, areaFC, areaCC;
areaFF = F1->overlappingArea(*F2);
areaFC = F1->overlappingArea(*C1);
areaCC = C1->overlappingArea(*C2);
return 0;
}
All the areas end up equaling 0.
I want areaFF = 1.1 , areaFC = 1.2 , areaCC = 2.2
Thanks for the help
Working code if you're interested
#include <iostream>
using namespace std;
class Rectangle;
class Circle;
class Shape {
public:
virtual double overlapwith(const Shape&)const = 0;
virtual double overlap(const Rectangle&)const = 0;
virtual double overlap(const Circle&)const = 0;
};
class Circle : public Shape {
public:
Circle() : x(0), y(0), r(0) {
}
Circle(int X, int Y, int R) : x(X), y(Y), r(R) {
}
double overlapwith(const Shape &with)const {
cout << "\nCirc::overlapwith(const Shap&)const";
return with.overlap(*this);
}
double overlap(const Rectangle &w)const {
cout << "\nCirc::overlap(const Rect&)const";
return 12;
}
double overlap(const Circle &w)const {
cout << "\nCirc::overlap(const Circ&)const";
return 11;
}
private:
int x, y, r;
};
class Rectangle : public Shape {
public:
Rectangle() : x(0), y(0), l(0), w(0) {
}
Rectangle(int X, int Y, int L, int W) : x(X), y(Y), l(L), w(W) {
}
double overlapwith(const Shape &with)const {
cout << "\nRect::overlapwith(const Shap&)const";
return with.overlap(*this);
}
double overlap(const Rectangle &w)const {
cout << "\nRect::overlap(const Rect&)const";
return 22;
}
double overlap(const Circle &w)const {
cout << "\nRect::overlap(const Circ&)const";
return 21;
}
private:
int x, y, l, w;
};
int main() {
Shape* F1 = new Rectangle(0,0,1,1);
Shape* F2 = new Rectangle(1,1,2,2);
Shape* C1 = new Circle(0,0,1);
Shape* C2 = new Circle(1,1,2);
double ff, fc, cf, cc;
ff = F1->overlapwith(*F2);
fc = F1->overlapwith(*C2);
cf = C1->overlapwith(*F2);
cc = C1->overlapwith(*C2);
cout << "\n\n\tff : " << ff
<< "\n\tfc : " << fc
<< "\n\tcf : " << cf
<< "\n\tcc : " << cc;
int pb; cin >> pb;
return 0;
}
Define two more pure virtual methods in the base class, in addition to the existing virtual methods, which can be pure. You will need to do some simple forward declarations:
class Rectangle;
class Circle;
class Shape {
public:
virtual double overlappingArea(const Shape&) const=0;
virtual double overlappingAreaWith(const Rectangle&) const=0;
virtual double overlappingAreaWith(const Circle&) const=0;
};
In each subclass, implement the first virtual method (the existing one) by invoking overlappingAreaWith() for the passed Shape & parameter, passing *this as the parameter:
class Rectangle {
// ...
double overlappingArea(const Shape &with) const override
{
return with.overlappingAreaWith(*this);
}
};
class Circle {
// ...
double overlappingArea(const Shape &with) const override
{
return with.overlappingAreaWith(*this);
}
};
Now, implement the other two overlappingAreaWith() methods in both Circle and Rectangle subclasses. They will now receive the other object, as either a Circle or a Rectangle parameter, as the case may be.
Each subclass is correctly implementing all three pure virtual methods.