208
Скрытая информация
Многие менеджеры памяти выделяют несколько дополнительных
байт в операторе
new
, чтобы при
вызове оператора
delete
был использован правильный размер независимо от того, является
деструктор виртуальным или нет.
void* Foo::operator new(size_t bytes)
{
size_t real_bytes = bytes + sizeof(size_t);
unsigned char* space = (unsigned char*)::operator new(real_bytes);
((size_t)space)
=
real_bytes;
return space + sizeof(size_t);
}
Теперь информацию о размере можно использовать в операторе
delete
или в любом другом месте, где
вам захочется узнать настоящий размер.
Тем не менее, при этой стратегии необходимо учесть ряд
обстоятельств.
Лишние затраты
В зависимости от компилятора и операционной системы эта методика уже может использоваться
незаметно для вас. Если вы самостоятельно добавите информацию о размере, она может
продублировать уже хранящиеся сведения. Скорее всего, это произойдет при делегировании
::operator new
на уровне объектов (см. выше). Такая методика приносит наибольшую пользу в
сочетании с блочными схемами выделения памяти, она сокращает затраты
::operator new
или
calloc
для блока, а не для отдельного объекта.
Оптимизация размера кванта
Большое преимущество этой методики заключается в том, что вы можете выделить больше места, чем
было запрошено. Как это? Может ли принести пользу выделение лишней неиспользуемой памяти? На
самом деле может, если подойти к этому разумно. Один из возможных вариантов — всегда выделять
память с приращением в n байт, где минимальное значение n равно 4. При это повышается вероятность
того, что после удаления 17-байтового объекта занимаемое им место удастся использовать, скажем, для
18- или 19-байтового объекта. Более изощренный подход состоит в выделении памяти по степеням 2,
или, если вы относитесь к числу истинных гурманов управления памятью, — по числам Фибоначчи.
Такие
системы называются системами напарников (buddy systems), поскольку для каждого
выделенного блока вы сможете найти его напарника (то есть другую половинку большого блока, из
которого он был выделен) исключительно по размеру и начальному адресу. Появляется возможность
эффективного воссоединения соседних освобожденных блоков. Если вы интересуетесь подобными
вещами, в главе 16 рассматриваются основы системы напарников для степеней 2 в контексте
автоматической сборки мусора.
Другая информация
Кроме размера блока может сохраняться и другая информация, например:
•
адрес объекта класса для данного объекта;
•
флаги блока (например, «флаг зомби», о котором будет рассказано ниже);
•
статистическая информация — например, время создания объекта.
Списки свободных блоков
Упрощенный
список свободных блоков, приведенный в предыдущей главе, может использоваться
только для объектов постоянного размера. Например, он не будет нормально работать с производными
классами, в которых добавляются новые переменные, поскольку они будут иметь другой размер. Сам
список тоже ограничивался одним размером; для передачи оператору
delete
правильного
размера
требовались виртуальные деструкторы. Впрочем, создать более универсальные списки уже не так уж
трудно.