Почему в распределителях C ++ нет функции перераспределения? - PullRequest
34 голосов
/ 23 июня 2010

В C стандартными функциями обработки памяти являются malloc(), realloc() и free(). Однако распределители stdlib в C ++ только параллельны двум из них: функция перераспределения отсутствует. Конечно, было бы невозможно сделать точно так же, как realloc(), потому что простое копирование памяти не подходит для неагрегированных типов. Но будет ли проблема, скажем, с этой функцией:

bool reallocate (pointer ptr, size_type num_now, size_type num_requested);

, где

  • ptr ранее был выделен тем же распределителем для num_now объектов;
  • num_requested> = num_now;

и семантика выглядит следующим образом:

  • если распределитель может расширить данный блок памяти на ptr с размера для num_now объектов до num_requested объектов, он делает это (оставляя дополнительную память неинициализированной) и возвращает true;
  • иначе он ничего не делает и возвращает false.

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

При наличии такой функции std::vector, скажем, может вырасти следующим образом (псевдокод):

if (allocator.reallocate (buffer, capacity, new_capacity))
  capacity = new_capacity;     // That's all we need to do
else
  ...   // Do the standard reallocation by using a different buffer,
        // copying data and freeing the current one

Распределители, которые вообще не способны изменять размер памяти, могут просто реализовать такую ​​функцию безоговорочно return false;.

Есть ли так мало реализации распределителя с поддержкой перераспределения, что не стоило бы этого беспокоить? Или есть какие-то проблемы, которые я упустил из виду?

Ответы [ 5 ]

19 голосов
/ 24 июня 2010

От: http://www.sgi.com/tech/stl/alloc.html

Это, пожалуй, самое сомнительное дизайнерское решение.Вероятно, было бы немного более полезно предоставить версию reallocate, которая либо изменила бы размер существующего объекта без копирования, либо вернула NULL.Это сделало бы его непосредственно полезным для объектов с конструкторами копирования.Это также позволило бы избежать ненужного копирования в случаях, когда исходный объект не был полностью заполнен.

К сожалению, это запретило бы использование realloc из библиотеки C.Это, в свою очередь, усложнило бы многие реализации распределителя и усложнило бы взаимодействие с инструментами отладки памяти.Таким образом, мы решили против этой альтернативы.

13 голосов
/ 24 июня 2010

Это на самом деле недостаток проекта, на который указывает Александреску со стандартными распределителями (не оператор new [] / delete [], а то, что изначально было распределителями stl, используемыми, например, для реализации std :: vector).

Realloc может происходить значительно быстрее, чем malloc, memcpy и free.Однако, хотя размер фактического блока памяти можно изменить, он также может переместить память в новое место.В последнем случае, если блок памяти состоит из не POD-ов, все объекты должны быть уничтожены и созданы после копирования после realloc.

Главное, что в стандартной библиотеке необходимо принять как ability - функция перераспределения как часть общедоступного интерфейса стандартного распределителя.Класс, подобный std :: vector, безусловно, мог бы использовать его, даже если реализация по умолчанию состоит в том, чтобы распределять блоки нового размера и освобождать старый.Это должна быть функция, способная разрушать и копировать объекты в памяти, хотя она не может обрабатывать память непрозрачным образом, если бы это делало это.В этом есть небольшая сложность, которая потребует дополнительной работы с шаблоном, возможно, поэтому он был исключен из стандартной библиотеки.

std :: vector <...> :: reserve недостаточно: он обращается кдругой случай, когда размер контейнера можно ожидать.Для списков действительно переменного размера решение realloc могло бы сделать смежные контейнеры, такие как std :: vector, намного быстрее, особенно если оно может иметь дело со случаями realloc, когда блок памяти был успешно изменен без перемещения, в этом случае он может опустить вызывающую копиюконструкторы и деструкторы для объектов в памяти.

8 голосов
/ 24 июня 2010

То, что вы просите, по сути, то, что делает vector::reserve.Без семантики перемещения для объектов невозможно перераспределить память и перемещать объекты без копирования и уничтожения.

3 голосов
/ 24 июня 2010

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

2 голосов
/ 23 июня 2010

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

Должно было быть realloc для выделения массива:

p = renew(p) [128];

или что-то в этом роде.

...