5.5.6 Небольшие Объекты
5.5.6 Небольшие Объекты
Когда вы используете много небольших объектов, размещамых в свободной памяти, то вы можете обнаружить, что ваша
программа тратит много времени выделяя и освобождая память под эти объекты. Первое решение – это обеспечить более хорший распределитель памяти общего назначения, второе для раработчика классов состоит в том, чтобы взять под контроль уравление свободной памятью для объектов некоторого класса с помощью подходящих конструкторов и деструкторов.
Рассмотрим класс name, который использовался в примерах table. Его можно было бы определить так:
struct name (* char* string; name* next; double value;
name(char*, double, name*); ~name(); *);
Программист может воспользоваться тем, что размещение и освобождение объектов заранее известного размера можно обрбатывать гораздо эффективнее (и по памяти, и по времени), чем с помощью общей реализации new и delete. Общая идея состоит в том, чтобы предварительно разместить «куски» из объектов name, а затем сцеплять их, чтобы свести выделение и освободение к простым операциям над связанным списком. Переменная nfree является вершиной списка неиспользованных name:
const NALL = 128; name* nfree;
Распределитель, используемый операцией new, хранит рамер объекта вместе с объектом, чтобы обеспечить правильную работу операции delete. С помощью распределителя, специализрованного для типа, можно избежать этих накладных расходов. Например, на моей машине следующий распределитель использует для хранения name 16 байт, тогда как для стандартного распрделителя свободной памяти нужно 20 байт. Вот как это можно сделать:
name::name(char* s, double v, name* n) (* register name* p = nfree; // сначала выделить
if (p) nfree = p-»next; else (* // выделить и сцепить name* q = (name*)new char[ NALL*sizeof(name) ]; for (p=nfree= amp;q[NALL-1]; q«p; p–) p-»next = p-1; (p+1)-»next = 0; *)
this = p; // затем инициализировать string = s; value = v; next = n; *)
Присвоение указателю this информирует компилятор о том, что программист взял себе управление, и что не надо использвать стандартный механизм распределения памяти. Конструктор name::name() обрабатывает только тот случай, когда name рамещается посредством new, но для большей части типов это всегда так. В #5.5.8 объясняется, как написать конструктор для обработки как размещения в свободной памяти, так и других видов размещения.
Заметьте, что просто как
name* q = new name[NALL];
память выделять нельзя, поскольку это приведет к бескнечной рекурсии, когда new вызовет name::name().
Освобождение памяти обычно тривиально:
name::~name() (* next = nfree; nfree = this; this = 0; *)
Присваивание указателю this 0 в деструкторе обеспечивет, что стандартный распределитель памяти не используется.