2

I need to know at execution-time the subtype of the base-class object I'm working with.

First of all I have my base-class called Element. Then, I have several subclasses called Car, Adult... all derived from Element. In some part of my code I create some objets of that types which I store in an Element array.

The problem comes when I get through the array and I try to recover the object's primary type.

I've been trying to do so using dynamic_cast but every Element can be cast successfully to Car, Adult, or whatever ...

This is the code:

Element elements[3];
Car c;
Adult a1;
Adult a2;
elements[0] = c;
elements[1] = a1;
elements[2] = a2;
int numElements = 3;
for(int i = 0; i< numElements; i++){
    if(dynamic_cast<Adult*>(&elements[i]) == nullptr){
        cout << "Its an adult" << endl;
    }else{
        cout << "Sth else" << endl;
    }
}

And that's the result:

Its an adult
Its an adult
Its an adult

This is the subclasses code:

class Car : public Element{
    ...
};

class Adult : public Element{
    ...
};
Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
cildoz
  • 436
  • 3
  • 14
  • 1
    Please [edit] your question to show a [mcve] demonstrating how you're creating the objects, how you're filling the array, how you cast the objects, and how the casts succeed. – Angew is no longer proud of SO Jun 03 '18 at 16:51
  • Sounds like you want either [is_base_of](http://en.cppreference.com/w/cpp/types/is_base_of) or [dynamic_cast](https://en.cppreference.com/w/cpp/language/dynamic_cast). – Jesper Juhl Jun 03 '18 at 17:06
  • @JesperJuhl I've tried both of them but they both say Its an adult when there's one is not – cildoz Jun 03 '18 at 17:10

1 Answers1

4

There are two problems with your code.

One, Element elements[3]; creates an array of Element objects. Not of "objects of classes derived from Element". You cannot store anything other than an Element in such an array, and any attempt to do so results in object slicing: the non-Element part of the source object is sliced away (discarded) and only the Element part makes it in.

If you want dynamic polymorphism, you have to use pointers or references, and usually also dynamic allocation. In your case, this could look something like this:

std::unique_ptr<Element> elements[3];
Car c;
Adult a1;
Adult a2;
elements[0] = std::make_unique<Car>(c);
elements[1] = std::make_unique<Adult>(a1);
elements[2] = std::make_uniuqe<Adult>(a2);

Or, if you want to avoid copying the objects:

std::unique_ptr<Element> elements[3];
auto c = std::make_unique<Car>();
auto a1 = std::make_unique<Adult>();
auto a2 = std::make_unique<Adult>();
elements[0] = std::move(c);
elements[1] = std::move(a1);
elements[2] = std::move(a2);

Or just create them directly in the array:

std::unique_ptr<Element> elements[3];
elements[0] = std::make_unique<Car>();
elements[1] = std::make_unique<Adult>();
elements[2] = std::make_unique<Adult>();

Two, you're misinterpreting the result of dynamic_cast. If dynamic_cast succeeds, it returns the pointer cast to the appropriate type. If it fails, it returns a null pointer. Your condition is reversed, as you're interpreting == nullptr as a success. The loop should actually look like this (with the unique_ptrs from point #1 in place):

for(int i = 0; i< numElements; i++){
    if(dynamic_cast<Adult*>(elements[i].get())){
        cout << "Its an adult" << endl;
    }else{
        cout << "Sth else" << endl;
    }
}

A few side notes:

  • Since your Element is used as a polymorphic base class, it should have a virtual destructor. Otherwise, using a pointer of type Element to delete a dynamically allocated object of a type derived from Element will result in Undefined Behaviour (most likely memory corruption).

  • If your Element is intended only as an abstract base and you never expect objects of type exactly Element (not derived from it) to exist, you should make Element abstract by giving it at least one pure virtual function (virtual void foo() = 0;). If it's a real base class, it will probably naturally contain a few candidates for that.

  • Needing to know the exact derived type when working with a base class is often a sign of bad design. Ideally, Element should contain virtual functions covering all functionality you may need to do with the objects in a type-abstracted manner. Exceptions exist, of course, but you should probably re-evaluate your design to make sure those dynamic_casts are actually necessary.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455