I can see 2 issues in your code:
- Since your classes are responsible for memory management, I would suggest to make your destructors
virtual
, because if you, at any point, will try to delete derived class object via base pointer, the destructors of derived classes will not be invoked. It shouldn't be a problem in your current code, but may become a problem in a future.
I.e:
int main ()
{
AA* aa = new BB (2);
delete aa;
}
Will not call the BB::~BB()
in your case.
- The problem that you are noticing, and writing this question about.
After you cast your variable of type from BB*
to AA*
(even though, the cast isn't necessary, you can straight-up assign, due to types being covariant) in line:
AA* cc = dynamic_cast<AA*>(bb);
Your variable cc
is treated as if it is of type AA*
(it doesn't matter that it has the runtime type of BB*
, in general case - you don't know, and should not care about the exact runtime type). On any virtual method call, they are dispatched to the correct type via the use of the vtable.
And now, why are you getting strange values printed in the console/segmentation fault? What's the result of cc->getA ()
? Since the variable cc
is treated as AA*
, the return value is A*
(as explained above, actual type is B*
, but, due to is-a relationship of inheritance is treated as A*
). What's the problem, you may ask: The array m_a
is the same size in both cases, right?
Well, not really, to explain that, I would need to explain how array indexing works in C++, and how it is related to sizes of the objects.
I guess, that I wouldn't shock you, stating that size of object of type B
(sizeof (B)
), is larger than that of type A
(sizeof (A)
), since B
has everything that A
has (due to inheritance), with some stuff of its own. On my machine sizeof(A)
= 16 bytes, and sizeof(B)
= 28 bytes.
So, when you create an array, the total amount of space that array takes up is [element_count] * [size of the element]
bytes, which seems logical. But, when you need to take an element from an array, it needs to figure, where exactly, that element is, in the memory, in all the space that array is taking up, so it does so, by calculating it. It does so as follows: [start of the array] + [index] * [size of element]
.
And, now we arrive at the source of the problem. You are trying to do cc->getA ()[1]
, but, since cc
, under the hood, is BB*
, so the size of AA::m_a
variable is 2 * sizeof (B)
(= 2 * 28 = 56
on my machine; first objects starts at offset 0
(0 * sizeof (B)
; second at offset 28
(1 * sizeof(B)
)), but since cc->getA ()
gets treated as A*, and you are trying to fetch second element from the array (index 1), it tries to fetch the object from the offset of
1 * sizeof (A)`, which, unfortunately, is in the middle of the space reserved to an object, and yet, any values can be printed/anything can happen - undefined behavior is invoked.
How to fix it? I would fix it by implementing the virtual indexing operators, instead of GetA
method on classes AA
/BB
, as follows:
class AA
{
public:
...
virtual A& operator[] (int idx)
{
return m_a[idx];
}
...
};
class BB : public AA
{
public:
...
virtual B& operator[] (int idx)
{
return dynamic_cast<B*>(m_a)[idx];
}
...
};
But, then you would need to be careful to call the operator on the object itself, and not to a pointer to object:
std::cout << cc->operator[](1).idx[0] << "\n";
std::cout << cc->operator[](1).idx[1] << "\n";
std::cout << cc->operator[](1).idx[2] << "\n";