Search code examples
c++inheritancevector

Understanding this case of polymorphism in C++


I have two classes, DerivedA and DerivedB that are derived from the same Base class. Both classes hold pointers to an object each, which is of one of two data types, TypeA and TypeB, respectively. Those data types are derived from the same basic type BaseType.

In the Base class, I have an (abstract) getter for those pointers. That getter is overridden in the each of the derived classes and specified such that they should return the derived data types.

In the main function, I define a vector that holds pointers to objects of both derived classes. But when I iterate over that vector, I only get BaseType pointers instead of the expected TypeA/TypeB pointers.

The following MWE illustrates that:

#include <iostream>
#include <vector>

using namespace std;

class BaseType {};
class TypeA : public BaseType {};
class TypeB : public BaseType {};

class Base {
public:
  virtual BaseType* GetObject() = 0;
};

class DerivedA: public Base {
public:
  TypeA* object = new TypeA();
  TypeA* GetObject() override {
    cout << "i'm comming from DerivedA" << endl;
    return object;
  }
};

class DerivedB: public Base {
public:
  TypeB* object = new TypeB();
  TypeB* GetObject() override {
    cout << "i'm comming from DerivedB" << endl;
    return object;
  }
};


int main() {
  vector<Base*> vec = {new DerivedA(), new DerivedB()};
  for (auto* item : vec){
    auto* obj_ptr = item->GetObject();
    cout << "I have an object of type " << typeid(obj_ptr).name() << endl;
  }
}

which yields the following output:

i'm comming from DerivedA
I have an object of type P8BaseType
i'm comming from DerivedB
I have an object of type P8BaseType

Now, I have two questions the answers to which I hope to help me to understand how polymorphism in C++ works:

  1. Why does the call to the GetObject() for each vector item clearly lands in the correct override, but does not return TypeA* or TypeB*, respectively, but a pointer to a BaseType instead?
  2. How would I need to alter the code to get pointers to types TypeA and TypeB, respectively?

Solution

  • There are two issues in your sample.

    First, BaseType is not polymorphic so typeid will not use RTTI. Adding a virtual function solves this.

    class BaseType {
    public:
        virtual ~BaseType() = default;
    };
    

    Second, typeid inspects the dynamic type of its operand if possible, but does not special-case pointers. obj_ptr itself is unequivocally of type BaseType *, even if it points to a derived object. *obj_ptr, on the other hand, is a BaseType & which typeid(*obj_ptr) will gladly query for its dynamic type.

    See the resulting code on Godbolt