Calling object method passing superclass where subclass expected - c++

For example i have three classes
class Shape
{
static bool collide(Box * a, Box * b)
static bool collide(Box * a, Sphere * b)
static bool collide(Sphere * a, Sphere * b)
};
class Box : public Shape{};
class Sphere : public Shape{};
and for example somewhere else i have this piece of code
Shape * a = new Box();
Shape * b = new Sphere();
Shape::collide(a, b);
The compiler raises and error.
How can i automatically downcast objects given as argument without adding too much logic?

The compiler can't cast the objects automatically, you will have to cast them manually, eg:
class Box;
class Sphere;
class Shape
{
virtual ~Shape(){}
static bool collide(Shape *a, Shape *b);
static bool collide(Box *a, Box *b);
static bool collide(Box *a, Sphere *b);
static bool collide(Sphere *a, Sphere *b);
};
class Box : public Shape{};
class Sphere : public Shape{};
bool Shape::collide(Shape *a, Shape *b)
{
if (Box *a_box = dynamic_cast<Box*>(a))
{
if (Box *b_box = dynamic_cast<Box*>(b))
return collide(a_box, b_box);
if (Sphere *b_sphere = dynamic_cast<Sphere*>(b))
return collide(a_box, b_sphere);
}
else if (Sphere *a_sphere = dynamic_cast<Sphere*>(a))
{
if (Sphere *b_sphere = dynamic_cast<Sphere*>(b))
return collide(a_sphere, b_sphere);
}
return false;
}
bool Shape::collide(Box *a, Box *b) { ... }
bool Shape::collide(Box *a, Sphere *b) { ... }
bool Shape::collide(Sphere *a, Sphere *b) { ... }
Shape * a = new Box();
Shape * b = new Sphere();
Shape::collide(a, b);
Needless to say, that can get a bit tedious as new shapes are added. You should instead add a single virtual method to Shape itself and let the derived classes override it to handle different types as needed, eg:
class Shape
{
virtual ~Shape(){}
virtual bool collide(Shape * b) { return false; };
};
class Box : public Shape{
bool collide(Shape * b) override {
if (Box *b_box = dynamic_cast<Box*>(b)) {
return ...;
}
if (Sphere *b_sphere = dynamic_cast<Sphere*>(b)) {
return ...;
}
return Shape::collide(b);
};
};
class Sphere : public Shape{
bool collide(Shape * b) override {
if (Sphere *b_sphere = dynamic_cast<Sphere*>(b)) {
return ...;
}
return Shape::collide(b);
};
};
Shape * a = new Box();
Shape * b = new Sphere();
a->collide(b);

Related

Why can't I declare a data member from another class private within my class definition

I am getting a compiler error saying that the data member Point p is private within the context, when I declare Point p as private within class circle. The code and compiler error are below.
#include<iostream>
#include<vector>
class Point
{
public:
Point(double a, double b)
{
x = a;
y = b;
}
virtual ~Point(){}
private:
double x;
double y;
};
The code for the class shape and circle are as follows:
class shapes {
public:
virtual Point centre() const = 0;
virtual void draw() const = 0;
virtual void rotate(int angle) const = 0;
virtual ~shapes(){}
};
class circle: public shapes {
public:
Point centre() const override { return p; }
void draw() const override { }
void rotate(int angle) const override {}
virtual ~circle() {}
circle(Point x, int r):p{x},radius{r}{}
private:
Point p;
int radius; };
Edit: Smiley face class inherits from circle class with code below:
class smiley: public circle
{ //smiley face is a circle + eyes and mouth
public:
smiley(Point p, int r):circle{p,r},mouth{nullptr}{}
Point centre() const override { return p;}
void draw() const override
{
//draw circle
circle::draw();
for(auto e:eyes)
{
e->draw();
}
mouth->draw();
}
void rotate(int angle) const {}
virtual ~smiley()
{
delete mouth;
for (auto eye : eyes) //why not delete [] eyes
{
delete eye;
}
}
private:
std::vector<shapes*> eyes; //smiley face has eyes
shapes* mouth; //smiley face has a mouth
};
If I make the data member p public in the class circle, everything works. The compiler error is listed below:
Why can I not define the Point object p, in the circle class private?
Edit: I have added the compiler error message and added the missing code asked for in the comments below. Would you be able to re-open the question?
Private class members can only be accessed within the class or by friends, so, if you would like it to be accessed outside the class by a non-friend, you would need to use a setter/getter.

C++ virtual inheritance - override of virtual function "" is ambiguous

I have a virtual inheritance example like below:
class Polygon {
public:
virtual double area() = 0;
};
class Rectangle : public virtual Polygon {
double a, b;
public:
Rectangle(double a, double b) {
this->a = a;
this->b = b;
}
double area() { return a * b; }
};
class Rombus : public virtual Polygon {
double a, h;
public:
Rombus(double a, double h) {
this->a = a;
this->h = h;
}
double area() { return a * h; }
};
class Square : public Rectangle, public Rombus {
public:
Square(double a) : Rectangle(a, a), Rombus(a, a) {}
};
It is one of requirements that Suare has to inherit from Rectangle and Rombus. That's why I use virtual inheritance.
But then I got an error:
override of virtual function "Polygon::area" is ambiguous
'Square': ambiguous inheritance of 'double Polygon::area(void)'
What am I doing wrong?
The error message is:
'Square': ambiguous inheritance of 'double Polygon::area(void)'
It should be obvious why: there are two implementations!
double area() { return a * b; } // in Rectangle
double area() { return a * h; } // in Rhombus
Square inherits both of them, so there is no possible way the compiler could know which to use.
You can "fix" it by overriding area() in Square as well.
This design is deficient from the start: a Square should only contain a single member, its width/height. But yours contains four members, all of which will always have the same value!

Correctly implementing inheritance

Given following classes:
class Geometry {
public:
double distanceBetweenGeometries(const Geometry& g);
private:
Shape myShape;
};
class Shape {
public:
double distance(const Shape& s1, const Shape& s2);
};
class Rectangle : public Shape {
private:
double i,j,length,width;
};
class Circle : public Shape {
private:
double i,j,radius;
};
So each geometry got a shape of type Rectangle or Circle. In my program I need to calculate the (Euclidean) distance between two geometries. Thus, given two geometries g1 and g2 I call
g1.distanceBetweenGeometries(g2);
and I want to return the distance between g1.myShape and g2.myShape.
I already know how to calculate the distance between two rectangles, two circles or between a rectangle and a circle. Somehow, I did not achieve an object-orientated solution for implementing the distance-function.
My idea is: Call the distance-function from a given geometry. This distance function calls the distance-function of a shape. In Shape::distance(..) I somehow need to differentiate of which type s1 and s2 are. Afterwards, I have to choose the correct mathematical formula to compute the distance between them. Can you tell me if my inheritance-idea is adequate here and how to implement the Shape::distance(..) function so that it can automatically determine which formula is requested for distance-computation?
You may do something like:
class Circle;
class Rectangle;
// Your existing methods to do the real computation:
double distanceRC(const Rectangle&, const Circle&);
double distanceRR(const Rectangle&, const Rectangle&);
double distanceCC(const Circle&, const Circle&);
class Shape {
public:
virtual ~Shape() = default;
virtual double distanceWith(const Shape&) const = 0;
virtual double distanceWith(const Rectangle&) const = 0;
virtual double distanceWith(const Circle&) const = 0;
};
class Rectangle : public Shape {
public:
double distanceWith(const Shape& s) const override { return s.distanceWith(*this); }
double distanceWith(const Rectangle& r) const override { return distanceRR(*this, r);}
double distanceWith(const Circle& c) const override { return distanceRC(*this, c); }
private:
double i,j,length,width;
};
class Circle : public Shape {
public:
double distanceWith(const Shape& s) const override { return s.distanceWith(*this); }
double distanceWith(const Rectangle& r) const override { return distanceRC(r, *this);}
double distanceWith(const Circle& c) const override { return distanceCC(*this, c); }
private:
double i,j,radius;
};

Storing classes in boost::variant

Someone recommended me to use boost::variant as shape variable to store different types of shapes in it. But, when implemented boost::variant to my code, I got an error while compiling. Error says: 'Shape': base class undefined and more errors.
Here is my code (Object.h):
using Shape = boost::variant<Rectangle, Circle>;
enum Shape_Type
{
RECTANGLE,
CIRCLE
};
struct Position
{
float x, y;
Position(float position_x, float position_y)
{
x = position_x;
y = position_y;
}
};
class Object : private Shape
{
private:
std::string name;
public:
Object() = default;
Object(std::string name, Rectangle rectangle) : name(name), Shape(rectangle)
{
}
Object(std::string name, Circle circle) : name(name), Shape(circle)
{
}
void setPosition(float, float);
void setAngle(float);
Shape* getShape()
{
Shape* shape = this;
return shape;
}
Position getPosition();
const std::string* getName()
{
return &name;
}
};
class Rectangle
{
private:
sf::RectangleShape rectangleshape;
public:
Rectangle() = default;
Rectangle(float width, float height)
: rectangleshape(sf::RectangleShape(sf::Vector2f(width, height)))
{
}
void setPosition(float position_x, float position_y)
{
rectangleshape.setPosition(position_x, position_y);
}
void setAngle(float angle)
{
rectangleshape.setRotation(angle);
}
sf::RectangleShape* getRectangleShape()
{
return &rectangleshape;
}
Position getPosition()
{
return Position(rectangleshape.getPosition().x,
rectangleshape.getPosition().y);
}
};
class Circle
{
private:
sf::CircleShape circleshape;
public:
Circle() = default;
Circle(std::string name, float radius)
: circleshape(sf::CircleShape(radius))
{
}
void setPosition(float position_x, float position_y)
{
circleshape.setPosition(position_x, position_y);
}
void setAngle(float angle)
{
circleshape.setRotation(angle);
}
sf::CircleShape* getCircleShape()
{
return &circleshape;
}
Position getPosition()
{
return Position(circleshape.getPosition().x,
circleshape.getPosition().y);
}
};
And btw is getShape() function good?
Variants are used for static polymorphism, so you don't need the base class at all (that's dynamic - or virtual - polymorphism).
The members in a variant typically do not share a common base class, so you wouldn't have the getShape function, or you'd need to template it:
template <typename T>
T const& getShape() const { return boost::get<T>(_shape); }

C++ classes inheritance

I have two classes:
class CEnemy : CObject
{
protected:
int hitPoints;
};
class COgro : public CEnemy
{
COgro::COgro() {hitPoints = 100}
};
and in other file I have class 'CRocket', which can collide with COgro, there is it function:
void CRocket::OnCollision(CObject *collisionObject)
{
if (typeid(*collisionObject) == typeid(COgro))
{
//collisionObject->hitPoints -= 10; ?? or what?
}
}
I want to shoot 10 times to ogro before it dies. How to do this?
I've already tried:
collisionObject->hitPoints -= 10;
(CEnemy)collisionObject->hitPoints -= 10;
but I can't compile it...how to edit this hitPoints value, but without changing '(CObject *collisionObject)'?
Thx
EDIT:
//===============================================================
//------------------------------------CLASS CRocket-----------------------
class CRocket : public CObject
{
protected:
void OnAnimate(scalar_t deltaTime);
void OnCollision(CObject *collisionObject);
void OnDraw(CCamera *camera);
public:
float pitch;
float distanceTravel;
CVector forward;
bool isExplosion;
CTexture *explosionTex;
CExplosion *explosion;
CRocket();
~CRocket();
void Load();
void Unload();
};
void CRocket::OnCollision(CObject *collisionObject)
{
if (typeid(*collisionObject) == typeid(COgroEnemy))
{
isExplosion = true;
velocity = CVector(0.0, 0.0, 0.0);
explosion = new CExplosion(500, position, 8.0, explosionTex->texID);
PlaySound();
}
}
//-----------------------------------------class CObject
class CObject : public CNode
{
protected:
virtual void OnAnimate(scalar_t deltaTime)
{
position += velocity * deltaTime;
velocity += acceleration * deltaTime;
}
virtual void OnDraw(CCamera *camera) {}
virtual void OnCollision(CObject *collisionObject) {}
virtual void OnPrepare()
{
ProcessCollisions(FindRoot());
}
public:
CVector position;
CVector velocity;
CVector acceleration;
scalar_t size;
bool isDead;
CObject() {isDead = false;}
~CObject() {}
...
...
...
}
//---------------------------------------class CEnemy
class CEnemy : public CObject
{
public:
int hitPoints;
protected:
float distFromPlayer;
float runSpeed;
AIState_t aiState;
virtual void OnProcessAI() {}
void OnCollision(CObject *collisionObject)
{
// if this enemy collides with another enemy
if (typeid(*collisionObject) == typeid(CEnemy))
{
modelState = MODEL_IDLE;
velocity = CVector(0.0, 0.0, 0.0);
}
// if this enemy collides with the terrain (always)
else if (typeid(*collisionObject) == typeid(CTerrain))
{
position.y = ((CTerrain*)collisionObject)->GetHeight(position.x, position.z) + size;
}
else
{
}
}
public:
CPlayer *player;
...
...
//----------------------------------class COgro-------------------------
class COgroEnemy : public CEnemy
{
protected:
void OnProcessAI();
void OnCollision(CObject *collisionObject);
void OnPrepare();
public:
COgroEnemy() { Load(); }
COgroEnemy(float x, float z) { position.x = x; position.z = z; Load(); }
~COgroEnemy() {}
void Load();
};
You'll need to cast the pointer to a pointer type CEnemy* (or a subclass), or the dereferenced pointer to a reference type CEnemy&. For maximum safety, I'd suggest dynamic_cast, rather than an evil C-style cast; although that's slightly paranoid since you're checking the type before casting.
// no checks, undefined behaviour if type is wrong
((CEnemy*)collisionObject)->hitPoints -= 10;
static_cast<CEnemy*>(collisionObject)->hitPoints -= 10;
// throws if type is wrong
dynamic_cast<CEnemy&>(*collisionObject).hitPoints -= 10;
// does nothing if type is wrong
if (CEnemy* enemy = dynamic_cast<CEnemy*>(collisionObject)) {
enemy->hitPoints -= 10;
}
You might combine that with the type check, rather than using typeid:
if (COgro * ogro = dynamic_cast<COgro*>(collisionObject)) {
ogro->hitPoints -= 10;
}
Note that this isn't exactly the same as your test: it will pass if the object is a subtype of COgro, while your test checks for an exact match.
You code is not compiling because you are trying to access a class's protected data member from an external source.
The collisionObject parameter is an instance of CObject, which does not have a hitPoints data member.
Also, when you pass around pointers to base classes to functions, the functions should assume that they can only access the interface or features of the base class.
You should write another overloaded method:
void CRocket::OnCollision(CEnemy& enemy);
Or move the hitPoints data member to the CObject class.