Бьерн Страуструп - Язык программирования С++. Главы 5-8
Страница 25. Небольшие объекты



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

 Если в вашей программе много небольших объектов, размещаемых в
 свободной памяти, то может оказаться, что много времени тратится
 на размещение и удаление таких объектов. Для выхода из этой
 ситуации можно определить более оптимальный распределитель памяти
 общего назначения, а можно передать обязанность распределения
 свободной памяти создателю класса, который должен будет
 определить соответствующие функции размещения и удаления.
      Вернемся к классу name, который использовался в примерах с
 table. Он мог бы определяться так:

          struct name {
             char* string;
             name* next;
             double value;

             name(char*, double, name*);
             ~name();

             void* operator new(size_t);
             void operator delete(void*, size_t);
           private:
             enum { NALL = 128 };
             static name* nfree;
           };

 Функции name::operator new() и name::operator delete() будут
 использоваться (неявно) вместо глобальных функций operator new()
 и operator delete(). Программист может для конкретного типа написать
 более эффективные по времени и памяти функции размещения и
 удаления, чем универсальные функции operator new() и
 operator delete(). Можно, например, разместить заранее "куски"
 памяти, достаточной для объектов типа name, и связать их в список;
 тогда операции размещения и удаления сводятся к простым операциям
 со списком. Переменная nfree используется как начало списка
 неиспользованных кусков памяти:

            void* name::operator new(size_t)
            {
              register name* p = nfree;  // сначала выделить

              if (p)
                 nfree = p->next;
              else {                // выделить и связать в список
                 name* q = (name*) new char[NALL*sizeof(name) ];
                 for (p=nfree=&q[NALL-1]; q<p; p--) p->next = p-1;
                 (p+1)->next = 0;
              }

              return p;
            }

 Распределитель памяти, вызываемый new, хранит вместе с объектом его
 размер, чтобы операция delete выполнялась правильно. Этого
 дополнительного расхода памяти можно легко избежать, если
 использовать распределитель, рассчитанный на конкретный тип. Так,
 на машине автора функция name::operator new() для хранения объекта
 name использует 16 байтов, тогда как стандартная глобальная
 функция operator new() использует 20 байтов.
     Отметим, что в самой функции name::operator new() память нельзя
 выделять таким простым способом:

          name* q= new name[NALL];

 Это вызовет бесконечную рекурсию, т.к. new будет вызывать
 name::name().
     Освобождение памяти обычно тривиально:

          void name::operator delete(void* p, size_t)
          {
             ((name*)p)->next = nfree;
             nfree = (name*) p;
          }

 Приведение параметра типа void* к типу name* необходимо, поскольку
 функция освобождения вызывается после уничтожения объекта, так что
 больше нет реального объекта типа name, а есть только кусок
 памяти размером sizeof(name). Параметры типа size_t в приведенных
 функциях name::operator new() и name::operator delete() не
 использовались. Как можно их использовать, будет показано в $$6.7.
 Отметим, что наши функции размещения и удаления используются
 только для объектов типа name, но не для массивов names.

 
« Предыдущая статья   Следующая статья »