# 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*`, а после — некорректное обратное приведение к типу класса:

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

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

    1 2 3 1 2 3

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

    1 2 3 2 3 -858993460

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

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

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

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

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

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

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

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

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

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

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

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

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

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

- Joost's Dev Blog. Hardcore C++: why "this" sometimes doesn't equal "this".

Данная диагностика классифицируется как:

CWE-704