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

Анализатор обнаружил опасные приведения указателя this к типу void\* и последующее обратное приведение void\* к типу класса\. Само по себе преобразование this к типу void\* не является ошибкой, но в ряде случаев ошибочным является обратное преобразование — от void\* к типу указателя на класс\. В результате таких преобразований возможно получение некорректного указателя\.

Рассмотрим пример, где используется приведение `this` к `void*`, а после — некорректное обратное приведение к типу класса:



```cpp
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();
}
```

Можно было бы предположить, что вывод будет следующим:

```cpp
1 2 3 1 2 3
```

Однако на самом деле на экране отобразится это:

```cpp
1 2 3 2 3 -858993460
```

В итоге вывод для всех данных после преобразований является некорректным\. Проблема кроется в том, что теперь указатель `pointerC` указывает не на начало объекта `C`, а на блок памяти, выделенной под объект `B`\.

Кажется, что такая ошибка маловероятна и её трудно допустить, однако она очевидна только из\-за того, что пример маленький и простой\. В реальных программах со сложными иерархиями классов можно легко запутаться\. Cложность в том, что если функцию `GetAddr()` расположить в классе `A`, то всё работает, а если в классе `B`, то нет\. Давайте разберёмся в ситуации подробнее\.

Чтобы было проще понять, из\-за чего возникла ошибка, необходимо знать, как конструируются и располагаются в памяти объекты классов, созданных в результате множественного наследования\.

Схематичный пример изображён на рисунке 1\.

![V725_ru/image1.png](https://import.viva64.com/docx/blog/V725_ru/image1.png)

Рисунок 1 — Расположение в памяти объекта класса, полученного путём множественного наследования\.

Из рисунка 1 видно, что объект класса `С` \(который и получен в результате множественного наследования\) состоит из объектов классов `A` и `B`, а также части объекта `C`\.

Указатель `this` содержит адрес начала выделенного под объект блока памяти\. На рисунке 2 изображены указатели `this` для всех трёх объектов\.



![V725_ru/image2.png](https://import.viva64.com/docx/blog/V725_ru/image2.png)

Рисунок 2 — Указатели `this` и блоки памяти\.

Так как объект класса `C` состоит из трёх частей, `this` для него будет указывать не на блок памяти, который добавлен дополнительно к базовым классам, а на начало всего непрерывного блока памяти\. То есть в данном случае указатели `this` для классов `A` и `C` совпадут\. 

Указатель `this` для объекта класса `B` указывает на начало выделенного под него блока памяти, но при этом адрес начала этого участка памяти будет отличен от адреса начала участка памяти, выделенной под объект класса `C`\.

Таким образом, при вызове метода `GetAddr()` будет возвращён адрес объекта `B`, и при обратном преобразовании этого указателя к типу `C*` будет получен некорректный указатель\.

Т\.е\., если бы функция `GetAddr()` располагалась в классе `A`, то всё бы работало так, как и ожидал программист\. Но если она расположена в `B`, то происходит сбой\.

Во избежание подобных ошибок необходимо продумать, действительно ли стоит выполнять приведение `this` к `void*`, и если да, то тщательно проверить иерархию наследования, а также дальнейшие операции обратного преобразования от `void*` к типу указателя на класс\.

**Дополнительные ссылки:**

1. Joost's Dev Blog\. [Hardcore C\+\+: why "this" sometimes doesn't equal "this"](http://joostdevblog.blogspot.com/2013/03/hardcore-c-why-this-sometimes-doesnt.html)\.