Unicorn with delicious cookie
Nous utilisons des cookies pour améliorer votre expérience de navigation. En savoir plus
Accepter
to the top
>
>
>
V725. Dangerous cast of 'this' to...
menu mobile close menu
Additional information
toggle menu Contents

V725. Dangerous cast of 'this' to 'void*' type in the 'Base' class, as it is followed by a subsequent cast to 'Class' type.

30 Jui 2015

The analyzer has detected a dangerous casting of the this pointer to the void* type followed by a cast of void* back to the class type. Casting this to void* is not an error, but in certain cases, casting void* back to the class pointer is. Such conversions may lead to an invalid pointer.

Here is an example where this is cast to void*, followed by an invalid cast back to the class type:

class A
{
public:
  A() : firstPart(1){}
  void printFirstPart() { std::cout << firstPart << " "; }
private:
  int firstPart;
};

class B
{
public:
  B() : secondPart(2){}
  void* GetAddr() const { return (void*)this;  }
  void printSecondPart() { std::cout << secondPart << " "; }
private:
  int secondPart;
};

class C: public A, public B
{
public:
  C() : A(), B(), thirdPart(3){}
  void printThirdPart() { std::cout << thirdPart << " "; }
private:
  int thirdPart;
};
void func()
{
  C someObject;

  someObject.printFirstPart();
  someObject.printSecondPart();
  someObject.printThirdPart();

  void *pointerToObject = someObject.GetAddr();
  ....
  auto pointerC = static_cast<C*>(pointerToObject);

  pointerC->printFirstPart();
  pointerC->printSecondPart();
  pointerC->printThirdPart();
}

One might assume that the output would be as follows:

1 2 3 1 2 3

However, the following is printed:

1 2 3 2 3 -858993460

As a result, the output for all data after conversions is incorrect. The issue is that now pointerC points to the memory block allocated for the B object rather than to the beginning of the C object.

This error may seem farfetched and unreal, but it is only obvious because the example above is short and simple. In real-life programs with complex class hierarchies, it may be far vaguer. The most confusing part is that the GetAddr() function works when it's in the A class, but fails when placed in the B class.

To easily understand what caused the error, programmers need to know how class objects created using multiple inheritance are constructed and arranged in memory.

A schematic example is shown in the Figure 1.

Figure 1—The memory layout of a class object created through multiple inheritance.

The C class object, obtained as a result of multiple inheritance, consists of the A and B class objects, as well as a part of the C object.

The this pointer points to the beginning of the memory block allocated for the object. The Figure 2 shows this pointers for all the three objects.

Figure 2—The this pointers and memory blocks.

Since the C class object consists of three parts, its this pointer points to the beginning of the entire continuous memory block rather than to an additional one. That is, this pointers for the A and C classes match in this case.

The this pointer for the B class object points to the beginning of the memory block allocated for it, but its address differs from the starting address of the memory block allocated for the C class object.

So, when calling the GetAddr() method, a programmer gets the address of the B object and then, after casting the resulting pointer back to the C* type , they get an incorrect pointer.

In other words, if the GetAddr() function were stored in the A class, the program would work as expected, but when it is stored in B, a developer gets an error.

To avoid such errors, programmers should consider whether they really need to cast this to void*. If the answer is yes, they need to carefully check the inheritance hierarchy and any casts of void* back to the class pointer type.

References:

This diagnostic is classified as: