Introduction
I have faced with one rather interesting bug which was related to a wrong usage of casts. It is a great pleasure for me to share the results of the research. So the details are below. (For more details please take a look at my other article)
Bug description
In one of the projects I was involved we had a class "C
" derived from two classes "A
" and "B
".
class A
{
public:
void funcA1();
void funcA2();
private:
int field1;
};
class B
{
public:
void funcB1();
void funcB2();
private:
int field1;
};
class C : public A, public B
{
public:
void funcC1();
void funcC2();
private:
int field1;
};
We had a vector of pointers on C
class and a function to find the index of a given pointer.
B* ptr = reinterpret_cast<B*>(cPointer);
int index = FindIndex(vectorOfCPointers, ptr);
Inside of FindIndex
function we had a following code:
...
for (size_t i = 0; i < ptrVector.size(); ++i)
{
if (ptr == static_cast<B*>(ptrVector.at(i)))
return i;
}
return -1;
...
The result of this function was always -1, so it was look like that there is no object even if it was there.
Fix
I think that it's easy to understand how to fix this bug. We should just change
reinterpret_cast
outside the function onto
static_cast
. We will get a different pointer values with a different casts. For example if cPtr
is equal to 0x11223344 as a result
of casts we will get a following values:
static_cast<B*>(cPtr)
will be 0x11223348
reinterpret_cast<B*>(cPtr)
will be 0x11223344
Explanation
Let's find out why did it
happened. First of all we should know that object without virtual functions is represented in memory like a simple structure. It has all its fields
placed in memory with the same order like they were declared in a class definition. For example if we have a class like this:
class SomeClass
{
int a;
int b;
int c;
};
And we have a pointer "ptr
" on the object of that class. We can have a direct
access to class fields like this.
int * aPtr = (int*)ptr;
int* bPtr = ((int*)ptr ) + 1;
int* cPtr = ((int*)bptr) + 1;
So what will happened in a case of multiple inheritance? For example we have our classes A, B, and C. Class C in memory will look like a sequence of classes A, B and C. And the pointer to the C class will point the sequence of fields of A class, fields of B class and finally fields of C class.
cPtr -> | Class A members |
| Class B members |
| Class C members |
So what should happen if we want to cast
cPtr
to the bPtr
? Right, we should add the size of A members to
cPtr
, so we will get bPtr
points on the B class.
| Class A members |
bPtr -> | Class B members |
| Class C members |
As we can see
static_cast
did that transformation but
reinterpret_cast
did not.