Я видел класс, который определяется следующим образом ..
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 член, и он размещен в куче?





Это старый трюк C, который использовался для обхода недоступности массивов переменной длины в обычном C. Да, он также работает в C++, если вы используете подходящие конструкции распределителя (например, выделение кучи необработанной памяти желаемого размера и затем размещение нового объекта там). Это безопасно, пока вы не переходите к концу выделенной памяти, но это действительно сбивает с толку по крайней мере некоторые отладчики памяти.
При использовании этого метода вы должны быть абсолютно уверены в том, что массив переменной длины является последним элементом в макете объекта, иначе вы перейдете к другим внутренним переменным.
Однако я немного сомневаюсь в реализации фабричной функции - я предполагаю, что параметр 'size' на самом деле является желаемым размером массива? Кроме того, не забывайте, что вам придется освободить указанную выше память, используя «бесплатно», а не «удалить», хотя последнее может работать в большинстве случаев.
Если нет веской причины, почему памятью нужно управлять таким образом, я бы просто заменил массив на std :: vector.
Я исходил из предположения, что вы не можете гарантировать порядок участников, поэтому мой вопрос о том, безопасно ли это только потому, что есть только 1 участник.
Компиляторы AFAIK не вмешиваются в структуру памяти (т. Е. Порядок членов - это порядок, в котором они объявлены), но, к сожалению, у меня нет соответствующей ссылки на C++, чтобы это было необходимо. Но для совместимости с C он должен соблюдать порядок объявления.
Насколько я читал по другим вопросам, это касается только POD-типов. Для non-pod, как в этом случае, вы не можете гарантировать.
Также обратите внимание: перед вызовом free () вы также должны вручную вызвать деструктор (НЕ через delete). Это потому, что вы используете для размещения новую версию new.
Это должно быть нормально для 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, такой метод был бы безопасным.
Педантично, использование
this->iBuf[2]было бы UB без ограничений доступа (даже если было выделено достаточно памяти).