Анализатор обнаружил потенциально возможную ошибку в коде, связанную с тем, что массив производных классов адресуется через указатель на базовый класс. При попытке доступа к ненулевому элементу массива через указатель на базовый класс произойдет ошибка.
Рассмотрим пример такого кода:
class Base { int buf[10]; public: virtual void Foo() { ... } virtual ~Base() { } }; class Derived : public Base { char buf[10]; public: virtual void Foo() override { ... } virtual ~Derived() { } }; .... size_t n = 5; Base *ptr = new Derived[n]; // <= .... for (size_t i = 0; i < n; ++i) (ptr + i)->Foo(); ....
В примере объявлены базовый класс "Base" и производный от него "Derived". Каждый объект этих классов будут занимать в памяти 48 и 64 байта соответственно (вследствие выравнивания классов по ширине 8 байт; компилятор MSVC, 64-bit). При "i >= 1" для обращения к ненулевому элементу необходимо каждый раз перемещать указатель на "i * 64" байта, но, поскольку массив адресуется указателем на базовый класс Base, смещение на самом деле будет вычисляться как "i * 48" байт.
Так должно было вычисляться смещение указателя:
Однако вычислено смещение указателя будет так:
Фактически, программа начинает работать с объектами, содержащими случайный набор данных.
Корректный вариант кода:
.... size_t n = 5; Derived *ptr = new Derived[n]; // <= .... for (size_t i = 0; i < n; ++i) (ptr + i)->Foo(); ....
Ошибочно также приводить указатель на указатель на производный класс к указателю на указатель на базовый класс:
.... Derived arr[3]; Derived *pDerived = arr; Class5 **ppDerived = &pDerived; .... Base **ppBase = (Derived**)ppDerived; // <= ....
Для правильного хранения массива объектов производного класса полиморфически необходимо размещать объекты следующим образом:
Корректный код при этом будет выглядеть следующим образом:
.... size_t n = 5; Base **ppBase = new Base*[n]; // <= for (size_t i = 0; i < n; ++i) ppBase[i] = new Derived(); ....
Если мы хотим подчеркнуть, что будем работать только с одним объектом, то можно написать так:
.... Derived *derived = new Derived[n]; Base *base = &derived[i]; ....
Такой код считается анализатором безопасным, и он не выдаёт предупреждение.
Не является также ошибкой применение указателя, который адресуется на массив объектов производного класса, содержащий один элемент.
.... Derived arr[1]; Derived *new_arr = new Derived[1]; Derived *malloc_arr = static_cast<Base*>(malloc(sizeof(Derived))); .... Base *base = arr; base = new_arr; base = malloc_arr; ....
Примечание. В случае одинакового размера базового и производного классов допускается адресоваться на массив объектов производного класса указателем на базовый класс, однако так делать не рекомендуется.
Данная диагностика классифицируется как:
|