# V777. A dangerous widening type conversion from an array of a derived class objects to a base class pointer.

Анализатор обнаружил потенциально возможную ошибку в коде, связанную с тем, что массив производных классов адресуется через указатель на базовый класс. При попытке доступа к ненулевому элементу массива через указатель на базовый класс произойдет ошибка.

Рассмотрим пример такого кода:

    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;
    ....

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

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

CWE-468 CERT-CTR56-CPP