Вопрос

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

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, такой метод был бы безопасным.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top