Класс переменного размера - C ++
-
06-07-2019 - |
Вопрос
Я видел класс, который является классом, который определен следующим образом ..
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();
Насколько я понимаю, эта функция использует размещение new для расширения этого класса. Р>
Безопасно ли это только потому, что в куче только 1 член, и он расположен в куче?
Решение
Это старый трюк на Си, который использовался для обхода недоступности массивов переменной длины в простом С. Да, он также работает в С ++, если вы используете подходящие конструкции распределителя (например, выделяя кучу необработанной памяти желаемый размер, а затем размещение нового объекта там). Это безопасно до тех пор, пока вы не бродите по концу выделенной памяти, но это может сбить с толку по крайней мере некоторых отладчиков памяти.
При использовании этого метода вы должны быть абсолютно уверены, что массив переменной длины является последним элементом в макете объекта, в противном случае вы будете обходить другие внутренние переменные.
Я, однако, немного сомневаюсь в реализации фабричной функции - я предполагаю, что параметр 'size' на самом деле является желаемым размером массива? Кроме того, не забывайте, что вам придется освобождать память, указанную выше, используя «free», а не «delete», хотя последний может работать в большинстве случаев.
Если нет веских причин, по которым память должна управляться таким образом, я бы просто заменил массив на std :: vector.
Другие советы
Это должно быть в порядке для 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:
Порядок, в котором подобъекты базового класса размещаются в наиболее производном объекте (1.8), не указан.
Если бы StringChild
был структурой POD, такой метод был бы безопасным.