I have the following small program:
#include <iostream>
#include <map>
using namespace std;
class A {
public:
virtual void hello(int i)
{
cout << "A Hello " << i << endl;
};
};
class B {
public:
virtual void nothing() = 0;
};
class C : public A, public B {
public:
virtual void hello(int i) override
{
cout << "C Hello " << i << endl;
};
virtual void nothing() override
{
cout << "C Nothing " << endl;
}
};
int main() {
map<int, B*> map_;
A* testA = new C();
map_[0] = (B*)testA;
B* myB = static_cast<B*>(map_[0]);
myB->nothing();
C* testC = new C();
map_[1] = (B*)testC;
myB = static_cast<B*>(map_[1]);
myB->nothing();
return 0;
}
As an output, I was expecting the following:
C Nothing
C Nothing
But here is what I get:
C Hello 0
C Nothing
So the wrong function is called: hello(int i) gets called even though it is never called in the code. I understand it has something to do with casts but I can't understand where is the mistake.
Why is hello(int i) called?
Definition: Memory corruption can be described as the vulnerability that may occur in a computer system when its memory is altered without an explicit assignment. The contents of a memory location are modified due to programming errors which enable attackers to execute an arbitrary code.
Pointer variable can point to a invalid memory location which can cause access violation and a crash. Memory corruption may occur because of poor array buffer handling or some abnormal runtime use-cases.
The conversion between base-class pointers (sometimes known as cross-casting) requires dynamic_cast
. Given a pointer to A
there's no way to know (statically) that it's part of a C
object, and therefore no way to statically find the corresponding B
subobject.
What you call a "regular" cast is the most dangerous type of cast. You should never use that style of cast (at least, not to convert pointers or references). It will force the conversion using any cast it can - apart from dynamic_cast
. So in this case, it's equivalent to reinterpret_cast
, pretending that there's a B
object at the same address as the A
object. Since there isn't a B
there (it's somewhere else within the C
), you get undefined behaviour.
(The specific flavour or undefined behaviour is probably that it uses the virtual table entry that would contain nothing
in a B
, but actually contains hello
, and so ends up calling that, with some undefined value as the argument. But of course, being undefined, anything could happen in principle.)
Do not use C-style casts, especially when working with inheritance hierarchies. In your case, instead of (B*)testA
you should be using dynamic_cast<B*>(testA)
, and then checking whether or not it returns NULL
. This is needed, because C++ compiler does not know whether type of object pointed to by testA
is inherited from B
, and if yes, how to adjust the pointer value to reflect that.
So what you run into is an undefined behavior, that may occur because C++ implementation uses either wrong vtable, or wrong offset into vtable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With