2

So I've got Shape.h, Rectangle.h, Circle.h and main.cpp.

Shape.h got:

class Shape{
public:
    Shape() {};
    ~Shape() { cout << "Destroy Shape."; };

    virtual double getArea() = 0;
    virtual double getCircumference() = 0;
};

Rectangle and Circle their respective code.

Now in main.cpp I do

Shape* newRectangle= new Rectangle(4, 8);
Shape* newCircle= new Circle(10);

All fine and dandy so far. Here's where I'm stumped. I know what I have to do, I just don't know HOW to do it.

I'm trying to write a function that checks if a Shape* Object belongs to Circle or not.

It goes something like this

if Shape* object belongs to Object-Type Circle, then
cout << "It's a Circle, bruh!";
else
cout << "Ain't a circle, yo!";

I started after googling with that:

void check(Shape & object){
    Circle& checkObject = dynamic_cast<Circle&>(object);

}

The function in main will be called via:

check(*newRectangle);
check(*newCircle);

But I haven't got a clue how to go on :(. Any help and explanation is appreciated. Thank you!

Grumpy ol' Bear
  • 725
  • 3
  • 15
  • 27
  • try http://stackoverflow.com/questions/500493/c-equivalent-of-instanceof – Gordon Sep 24 '15 at 18:22
  • 1
    Make class `Shape` polymorphic. That means it has at least one virtual function. Usually the destructor is made virtual. Then you can use the C++ RTTI functionality, `typeid` and `dynamic_cast`. – Cheers and hth. - Alf Sep 24 '15 at 18:23
  • Make `~Shape()` `virtual`. That ensures `delete shp;` deletes whole class – mksteve Sep 24 '15 at 18:32

2 Answers2

6
  1. Try to avoid logic based on derived types. Use virtual member functions whenever you can.

    class Shape {
       public:
          virtual void print(std::ostream& out) const = 0;
       ...
    };
    

    and implement the function in the derived clases.

    class Circle : public Shape {
       public:
          virtual void print(std::ostream& out) const
          {
             out << "Got a Circle.\n";
          }
    
       ....
    };
    
  2. If you cannot solve your problem using a virtual member function, you'll need to resort to dynamic_cast.

    Shape* shapePtr = <some pointer>;
    Circle* circlePtr = dynamic_cast<Circle*>(shapePtr);
    if ( circlePtr ) {
       // Access Circle functionality.
    }
    
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • I'm at solution 2 right now. Can't do otherwise. I'm doing really hard to understand pointers. Bleh. What do I give into ? Taken from my example that I have created 2 shape pointers?? objects?? like Shape* newRectangle= new Rectangle(4, 8); and Shape* newCircle= new Circle(10); – Grumpy ol' Bear Sep 24 '15 at 18:41
  • I tried http://hastebin.com/dalanasudu.xml or I misunderstood something. But it tells me "cannot use 'dynamic_cast' to convert from 'Shape &' to 'Circle *'" – Grumpy ol' Bear Sep 24 '15 at 18:43
  • @NoCanDo You should write `Circle* circlePtr = dynamic_cast(&object);` in your hastebin example to convert a reference to a pointer. – Sergey Kalinichenko Sep 24 '15 at 18:48
4

You can do it by dynamically casting the Shape pointer to a pointer of desired type, and checking for nullptr:

if (dynamic_cast<Circle*>(object)) {
    cout << "It's a Circle, bruh!";
}

This requires that Runtime Type Information is turned on, and that your object has at least one virtual function (it does).

Note 1: if you have to do something like that, see if you could change your design so that you wouldn't have to do this. For example, you could apply a multiple dispatch pattern, such as Visitor, to avoid this check:

struct ShapeVisitor {
    virtual ~ShapeVisitor() {}
    virtual void visitCircle(const Circle* circle);
    virtual void visitRectangle(const Rectangle* rectangle);
};

class Shape{
public:
    Shape() {};
    virtual ~Shape() { cout << "Destroy Shape."; };

    virtual double getArea() = 0;
    virtual double getCircumference() = 0;
    virtual void Accept(const ShapeVisitor& visitor) = 0;
};

struct Circle : public Shape {
    virtual void Accept(const ShapeVisitor& visitor) {
        visitor.visitCircle(this);
    }
};

struct Rectangle : public Shape {
    virtual void Accept(const ShapeVisitor& visitor) {
        visitor.visitRectangle(this);
    }
};

Now you can print the kind of shape you see like this:

struct PrintTypeVisitor : public ShapeVisitor {
    virtual void visitCircle(const Circle* circle) {
        cout << "It's a Circle, bruh!";
    }
    virtual void visitRectangle(const Rectangle* rectangle) {
        cout << "It's a Rectangle, bruh!";
    }
}
...
PrintTypeVisitor v;
Shape* object = ...
object.Accept(v); // This prints dynamic type of the object

Note 2: You need to make Shape's destructor virtual.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • Yea, I was thinking about something link that. Null or nullpointer. Hmm...gonna try it. Thanks! – Grumpy ol' Bear Sep 24 '15 at 18:29
  • Doesn't the destructor on the base class need to be virtual for deletion through a base class pointer to work correctly? – James Adkison Sep 24 '15 at 18:39
  • @JamesAdkison Yes, thanks for the comment! I copied OP's code, but missed the non-`virtual` destructor. – Sergey Kalinichenko Sep 24 '15 at 18:42
  • Thank you, this solution is elegant, but not what I'm trying to understand right now. I'm trying to understand how http://pastebin.com/BxjEBfg9 works. Once I get that, I'll try your method :). It doesn't help me learning new methods while not understanding others. – Grumpy ol' Bear Sep 24 '15 at 18:49
  • @NoCanDo It works because the compiler squirrels away an extra piece of information with each object, which can be used to see if it is OK to cast through a base pointer (i.e. a `Share`) to a subclass (i.e. a `Circle` or a `Rectangle`). `dynamic_cast` operation checks this extra piece of information, and either produces a correct pointer if the cast is valid, or returns `nullptr` if the cast would be invalid. – Sergey Kalinichenko Sep 24 '15 at 18:53