Подтвердить что ты не робот

Должен ли это называться какой-то особый случай обрезки объектов?

Скажем, у меня есть класс Derived, который происходит из класса Base, тогда как sizeof(Derived) > sizeof(Base). Теперь, если вы выделяете массив Derived следующим образом:

Base * myArray = new Derived[42];

а затем пытается получить доступ к n -th объекту, используя

doSomethingWithBase(myArray[n]);

Тогда это может, вероятно, (но не всегда) вызвать поведение undefined из-за доступа к Base из недопустимого местоположения.

Каков правильный термин для такой ошибки программирования? Следует ли рассматривать случай разбиения объектов?

4b9b3361

Ответ 1

Это не разбиение объектов.

Как отмечалось, индексирование myArray не вызывает разбиение объектов, но приводит к поведению undefined, вызванному индексированием в массив Derived, как если бы это был массив Base.

Вид "ошибки распада массива".

Ошибка, введенная при назначении new Derived[42] to myArray, может быть изменением ошибки распада массива.

В истинном экземпляре такого типа ошибок существует реальный массив:

Derived x[42];
Base *myArray = x;

Проблема вводится потому, что массив Derived распадается на указатель на Derived со значением, равным адресу его первого элемента. Распад позволяет правильному выполнению указателя. Это поведение распада унаследовано от C, который был функцией разработки языка, позволяющей массивам "передаваться по ссылке".

Это приводит нас к еще худшему воплощению этой ошибки. Эта функция дает семантику C и С++ для синтаксиса массивов, которые превращают аргументы функции массива в псевдонимы для аргументов указателя.

void foo (Base base_array[42]) {
    //...
}

Derived d[42];
foo(d);          // Boom.

Однако new[] на самом деле является перегруженным оператором, который возвращает указатель на начало выделенного объекта массива. Таким образом, это не истинный экземпляр распада массива (хотя используется распределитель массива). Тем не менее, признаки ошибки одинаковы, а намерение new[] - получить массив Derived.

Обнаружение и устранение ошибок.

Используйте интеллектуальный указатель.

Эту проблему можно избежать, используя объект интеллектуального указателя вместо управления необработанным указателем. Например, аналогичная ошибка кодирования с unique_ptr будет выглядеть так:

std::unique_ptr<Base[]> myArray = new Derived[42];

Это приведет к ошибке времени компиляции, поскольку конструктор unique_ptr explicit

Используйте контейнер и, возможно, std::reference.

В качестве альтернативы вы можете избежать использования new[] и использовать std::vector<Derived>. Затем вы вынуждены были бы разработать другое решение для отправки этого массива в код рамки, который известен только Base. Возможно, функция шаблона.

void my_framework_code (Base &object) {
    //...
}

template <typename DERIVED>
void my_interface(std::vector<DERIVED> &v) {
    for (...) {
        my_framework_code(v[i]);
    }
}

Или, используя std::reference_wrapper<Base>.

std::vector<Derived> v(42);
std::vector<std::reference_wrapper<Base>> myArray(v.begin(), v.end());

Ответ 2

Это не разрезание вообще, скорее это поведение undefined, потому что вы обращаетесь к объекту Derived, где ни один не существует (если вам не повезет, и размеры выстраиваются в линию, и в этом случае он все еще UB, но может сделать что-то полезное в любом случае).

Это простой случай неудачной арифметики указателя.

Ответ 3

Это не клонирование объектов каким-либо образом.

Обрезка объектов отлично определена стандартом С++. Это может быть нарушение объектно-ориентированных принципов дизайна или что-то еще, но это не является нарушением правил С++.

Этот код нарушает 5.7 [expr.add] пункт 7:

Для сложения или вычитания, если выражения P или Q имеют тип "указатель на cv T", где T отличается от типа элемента cv-unqualified типа, поведение undefined, [Примечание. В частности, указатель на базовый класс не может использоваться для арифметики указателя, когда массив содержит объекты производного типа класса. -end note].

Оператор индекса массива определяется как эквивалентный арифметике указателя, 5.2.1 [expr.sub] пункт 1:

Выражение E1[E2] идентично (по определению) до *((E1)+(E2))

Ответ 4

Это не случай нарезки, хотя он очень похож. Нарезка хорошо определена. Это просто поведение undefined (всегда, а не только вероятность) из-за неправильной арифметики указателя.