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".
Данная диагностика классифицируется как: