Класс переменного размера - C++

Я видел класс, который определяется следующим образом ..

class StringChild : public StringBase
    {
public:
    //some non-virtual functions
    static StringChild* CreateMe(int size);
private:
    unsigned char iBuf[1];
    };

Статическая фабричная функция имеет следующую реализацию.

return new(malloc(__builtin_offsetof(StringChild ,iBuf[size]))) StringChild();

Насколько я понимаю, эта функция использует новое размещение для расширения этого класса.

Это безопасно только потому, что есть только 1 член, и он размещен в куче?

Педантично, использование this->iBuf[2] было бы UB без ограничений доступа (даже если было выделено достаточно памяти).

Jarod42 13.07.2018 15:59
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
2 292
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Это старый трюк C, который использовался для обхода недоступности массивов переменной длины в обычном C. Да, он также работает в C++, если вы используете подходящие конструкции распределителя (например, выделение кучи необработанной памяти желаемого размера и затем размещение нового объекта там). Это безопасно, пока вы не переходите к концу выделенной памяти, но это действительно сбивает с толку по крайней мере некоторые отладчики памяти.

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

Однако я немного сомневаюсь в реализации фабричной функции - я предполагаю, что параметр 'size' на самом деле является желаемым размером массива? Кроме того, не забывайте, что вам придется освободить указанную выше память, используя «бесплатно», а не «удалить», хотя последнее может работать в большинстве случаев.

Если нет веской причины, почему памятью нужно управлять таким образом, я бы просто заменил массив на std :: vector.

Я исходил из предположения, что вы не можете гарантировать порядок участников, поэтому мой вопрос о том, безопасно ли это только потому, что есть только 1 участник.

Dynite 09.01.2009 14:53

Компиляторы AFAIK не вмешиваются в структуру памяти (т. Е. Порядок членов - это порядок, в котором они объявлены), но, к сожалению, у меня нет соответствующей ссылки на C++, чтобы это было необходимо. Но для совместимости с C он должен соблюдать порядок объявления.

Timo Geusch 09.01.2009 16:22

Насколько я читал по другим вопросам, это касается только POD-типов. Для non-pod, как в этом случае, вы не можете гарантировать.

Dynite 09.01.2009 16:51

Также обратите внимание: перед вызовом free () вы также должны вручную вызвать деструктор (НЕ через delete). Это потому, что вы используете для размещения новую версию new.

Martin York 09.01.2009 19:57

Это должно быть нормально для POD при условии, что iBuf является последним членом структуры. Проблемы с не-POD могут быть такими, например. компилятор может переупорядочивать общедоступные / частные / защищенные члены, виртуальные базовые классы оказываются в конце наиболее производного объекта IIUC и т. д.

Ваша структура не является POD (у нее есть базовый класс), поэтому я бы ее не рекомендовал.

Кроме того, если вы создаете такие экземпляры

return new(malloc(__builtin_offsetof(StringChild ,iBuf[size]))) StringChild();

Вы должны убедиться, что память, полученная malloc, должна быть освобождена с помощью free, поэтому удалите ваши экземпляры следующим образом:

obj->~StringChild();
free(obj);

Может быть, вы хотите использовать ::operator new() для распределения

Строго говоря, поскольку StringChild является производным от StringBase, это небезопасно. Стандарт C++ не определяет макет для подобъектов базового класса. Пункт 10 абзац 3:

The order in which the base class subobjects are allocated in the most derived object (1.8) is unspecified.

Если бы StringChild был структурой POD, такой метод был бы безопасным.

Другие вопросы по теме