I was curious about about how dynamic_cast can cast an element of an array to a larger class (does it shift all the other elements ?). So I wrote a small code to try it. But I was suprised as it compiles but segfault at the first line. Why ?
#include <iostream>
class A
{
public:
virtual ~A() {}
};
class B : public A
{
public:
int x;
};
class C : public A
{
public:
int x;
int y;
};
int main()
{
A* aArray = new B[2];
(dynamic_cast<B&>(aArray[0])).x = 1; //segfault here
(dynamic_cast<B&>(aArray[1])).x = 2;
(dynamic_cast<C&>(aArray[0])).y = 3;
std::cout << (dynamic_cast<B&>(aArray[1])).x << std::endl;
return 0;
}
Here I go. I compiled and run with gdb
First I set the print object option:
(gdb) set print object
check the address of aArray
(gdb) print aArray
$1 = (B *) 0x8003a404
check the size of A and B
(gdb) print sizeof(B)
$2 = 8
(gdb) print sizeof(A)
$3 = 4
get the address of aArray[0]
(gdb) print &aArray[0]
$4 = (B *) 0x8003a404
get the address of aArray[1]
(gdb) print &aArray[1]
$5 = (A *) 0x8003a408
Quoting the answer in linked question:
if you look at the expression
p[1]
,p
is aBase*
(Base is a completely-defined type) and 1 is an int, so according to ISO/IEC 14882:2003 5.2.1 [expr.sub] this expression is valid and identical to*((p)+(1))
and
From 5.7 [expr.add] / 5, when an integer is added to a pointer, the result is only well defined when the pointer points to an element of an array object and the result of the pointer arithmetic also points the an element of that array object or one past the end of the array. p, however, does not point to an element of an array object, it points at the base class sub-object of a Derived object. It is the Derived object that is an array member, not the Base sub-object.
In this particular case, the implementation of the pointer arithmetic is to increase the pointer memory by the size of the pointer type (but refer to this answer for additional nuances).
The effect of aArray + 1
is to point at the 2nd element of an array of objects of type A
That matches:
(gdb) print (A*)(((char *)aArray) + sizeof(A))
$6 = (A *) 0x8003a408
... however aArray
is really an array of objects of type B.
So the second element of the array is:
(gdb) print &((B *)aArray)[1]
$6 = (B *) 0x8003a40c
so you ended up pointing somewhere in the middle of the first B object... and that access caused the segmentation fault.
For an alternative explanation an example refer to: [http://www.parashift.com/c++-faq/array-derived-vs-base.html]