Top.Mail.Ru
Unicorn with delicious cookie
Мы используем куки, чтобы пользоваться сайтом было удобно.
Хорошо
to the top
>
>
>
V725. Dangerous cast of 'this' to...
menu mobile close menu
Проверка проектов
Дополнительная информация
toggle menu Оглавление

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

30 Июн 2015

Анализатор обнаружил опасные приведения указателя 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* к типу указателя на класс.

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

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

close form

Заполните форму в два простых шага ниже:

Ваши контактные данные:

Шаг 1
Поздравляем! У вас есть промокод!

Тип желаемой лицензии:

Шаг 2
Team license
Enterprise license
** Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности
close form
Запросите информацию о ценах
Новая лицензия
Продление лицензии
--Выберите валюту--
USD
EUR
RUB
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

close form
Бесплатная лицензия PVS‑Studio для специалистов Microsoft MVP
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

close form
Для получения лицензии для вашего открытого
проекта заполните, пожалуйста, эту форму
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

close form
Я хочу принять участие в тестировании
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

close form
check circle
Ваше сообщение отправлено.

Мы ответим вам на


Если вы так и не получили ответ, пожалуйста, проверьте, отфильтровано ли письмо в одну из следующих стандартных папок:

  • Промоакции
  • Оповещения
  • Спам