0
votes

Say I have a Person class. This person class holds a Shape.

The Shape parent class has several child classes derived from it. For example Rectangle and Circle. Each of these child classes has their own methods. For example the Circle class has GetRadius(), while the Rectangle class has GetWidth() and more methods specific to that kind of shape.

Now let's say I have several people, some of which hold Rectangles and others hold Circles. I want to know what shape every person is holding and I want to get the information from those shapes. However I can't do this because the Person is holding a Shape, so it can't access any of the child specific methods.

I read something about casting but I found it to be a bit confusing and I was not sure whether or not casting would be the best way to do this, or whether there is a more efficiënt way to go about this entirely. So how could I do this?

Edit 1: Edit for more clarification. I want the methods such as GetWidth() and GetRadius() to return different types.

3
As a quick pointer to what you want to be using to solve these issues. You need to use dynamic binding. In C++ this is implemented with pointers, and virtual keyword. In such a case, you would have to call the functions that provide information GetRadius() and GetWidth() the same name in each child class e.g getShapeInfo(). getShapeInfo() would also be defined in Shape. You would probably make getShapeInfo() a pure virtual function. Meaning Shape is a abstract class. This is because, there exists no shape in the world, only things of a shape type, like a circle.izaak_pyzaak
so 'virtual double getShapeInfo()` in Shape and each Person would have a Shape *s to allow dynamic binding, then when either the Circle or Rect is pointed too calling s->getShapeInfo() will call either the circle or rectangle function, depending on the type of the object pointed to by sizaak_pyzaak
@izaak_pyzaak What if they return different types though? getShapeInfo() from Circle would return the radius in double, however I would like to have the getShapeInfo() from Rectangle to return something like a vec2 holding the width and height.JohnCake
You could define a new type ShapeInfo that returns from both. Although, this is somewhat shifting the problem of reflection onto a new type, to make the function call look neater. But you don't know who will hold what shape, this may be the way to go. Was the thing you read about casting about the dangers of down casting? Ultimately if you have two distinct functions and call getRadius on a rectangle, bad things will ensue.izaak_pyzaak
@JohnCake You have some answers which should answer most of your questions. Especially look at the one Sam Varshavchik provided.James Adkison

3 Answers

0
votes

With the Shape superclass having at least one virtual method you can certainly use dynamic_cast, to figure out which subclass each instance of the superclass is. That's certainly possible, and sometimes it's the right thing to do, but most of the time it's not.

Rather, the right thing to do is to design your classes in advance, in a manner that whatever operations can possibly be done with any Shape there's a Shape method for it, usually virtual. The lowest common denominator is to have a pure virtual method that's implemented by every subclass, which returns an identifier for which particular subclass this is.

But, generally, if you have a need to know what particular subclass an instance of a superclass is, then it's a clue that the class hierarchy is not designed correctly. It is true that life is not 100% perfect, all the time. Sometimes it might become necessary to resort to this kind of ugliness, in order to handle a tricky situation. But that shouldn't be the plan from the beginning. You should try to properly design your class hierarchy so this is not necessary.

Even the lowest common denominator of:

A) The aforementioned function that returns some enumerated identifier for which particular subclass the superclass is, and

B) The superclass defining all possible virtual methods that all superclasses could implement, with the default implementation that throws an exception

would make it possible to do this in a way that does not involve ugly casts.

0
votes

If you have an pointer of type Shape, which holds objects of child types such as Rectangle or Circle, then you can use dynamic_cast to check the type of object at runtime. dynamic_cast will allow you to safely check the run-time type of object whose address is held by a pointer.

Please look https://en.wikipedia.org/wiki/Run-time_type_information#dynamic_cast for an example of how this is done.

0
votes

You can avoid the need for casting by declaring virtual functions on the Shape class for any functions which are applicable to all derived classes. In this case, you want to know what each derived type is so you could have a getType() function. You don't have to do it exactly this way but the following is an example of how you could do it.

Example Code

#include <iostream>
#include <memory>
#include <string>
#include <vector>

class Shape
{
public:
    virtual ~Shape() {}

    virtual Shape* clone() const = 0;

    virtual std::string getType() const = 0;
};

class Circle : public Shape
{
public:
    virtual Circle* clone() const override { return new Circle(*this); }

    virtual std::string getType() const override { return "Circle"; }
};

class Rectangle : public Shape
{
public:
    virtual Rectangle* clone() const override { return new Rectangle(*this); }
    virtual std::string getType() const override { return "Rectangle"; }
};

class Person
{
public:
    explicit Person(const std::string& name, const Shape& shape) :
        mName(name),
        mShape(shape.clone())
    {
    }

    std::string mName;
    std::unique_ptr<Shape> mShape;
};

int main()
{
    std::vector<Person> people;
    people.emplace_back("Foo", Circle());
    people.emplace_back("Bar", Rectangle());

    for (const auto& person : people)
    {
        std::cout << person.mName << " with " << person.mShape->getType() << "\n";
    }

    return 0;
}

Example Output

Foo with Circle
Bar with Rectangle

Live Example