Введение в язык Си++

         

Небольшие Объекты


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

Рассмотрим класс 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=q[NALL-1]; qnext = 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 в деструкторе обеспечивает, что стандартный распределитель памяти не используется.



Содержание раздела