Matthias Matthias - 3 months ago 25
C++ Question

Convert array of pointers of derived class to array of base class pointers

Consider an inheritance hierarchy like this:


A
/ \
B1 B2
\ /
C
|
D

Realized in C++ like so:

class A {
public:
A() {};
virtual ~A() = 0;
double a;
};

A::~A() {};

class B1 : virtual public A {
public:
B1() {}
virtual ~B1() {}
double b1;
};

class B2 : virtual public A {
public:
B2() {}
virtual ~B2() {}
double b2;
};

class C : public B1, public B2 {
public:
C() {}
virtual ~C() {}
double c;
};

class D : public C {
public:
D() {}
virtual ~D() {}
double d;
};


Now, obviously I can do something like this:

D *d = new D();
A *a = (A*) d;
D *d_down = dynamic_cast<D*>(a);
assert(d_down != NULL); //holds


However, I can't seem to figure out how to get same behavior using arrays. Please consider the following code sample to see what I mean by that:

D *d[10];
for (unsigned int i = 0; i < 10; i++) {
d[i] = new D();
}

A **a = (A**) d;
D *d_down = dynamic_cast<D*>(a[0]);
assert(d_down != NULL); //fails!


So my questions would be:


  • Why does to above assertion fail?

  • How can I achieve the desired behavior?

  • I noticed, by chance, that the dynamic_cast above works if I remove the double fields from classes A through D. Why is that?


Answer

The problem is, that (A*)d is not numerically equal to d!

See, you have an object like

+-----------------+
| D:              | <----- d points here
| D vtable ptr    |
| +-------------+ |
| | A:          | | <----- but (A*)d points here!
| | A vtable ptr| |
| | ...         | |
| +-------------+ |
| +-------------+ |
| | B:          | |
| | ...         | |
| +-------------+ |
| +-------------+ |
| | C:          | |
| | ...         | |
| +-------------+ |
| ...             |
+-----------------+

When you cast a D* to A*, via static_cast or dynamic_cast, the compiler will inject the necessary arithmetic for you.

But when you cast it via reinterpret_cast, or cast a D** to A**, which is the same thing, the pointer will keep its numeric value, because the cast does not give the compiler the right to dereference the first layer to adjust the second layer.

But then the pointer will still point at D's vtable, not A's vtable, and therefore won't be recognized as A.