Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
КРАТКИЙ ОБЗОР С.doc
Скачиваний:
1
Добавлен:
26.10.2018
Размер:
2.11 Mб
Скачать

5.5.5 Массивы объектов класса

      Чтобы можно было описать массив объектов класса с конструктором,       этот класс должен иметь стандартный конструктор, т.е. конструктор,       вызываемый без параметров. Например, в соответствии с определением       table tbl[10];       будет создан массив из 10 таблиц, каждая из которых инициализируется       вызовом table::table(15), поскольку вызов table::table() будет       происходить с фактическим параметром 15.       В описании массива объектов не предусмотрено возможности указать       параметры для конструктора. Если члены массива обязательно надо       инициализировать разными значениями, то начинаются трюки с       глобальными или статическими членами.       Когда уничтожается массив, деструктор должен вызываться для       каждого элемента массива. Для массивов, которые размещаются не       с помощью new, это делается неявно. Однако для размещенных в свободной       памяти массивов неявно вызывать деструктор нельзя, поскольку транслятор       не отличит указатель на отдельный объект массива от указателя на начало       массива, например:       void f()       {       table* t1 = new table;       table* t2 = new table[10];       delete t1; // удаляется одна таблица       delete t2; // неприятность:       // на самом деле удаляется 10 таблиц       }       В данном случае программист должен указать, что t2 - указатель       на массив:       void g(int sz)       {       table* t1 = new table;       table* t2 = new table[sz];       delete t1;       delete[] t2;       }       Функция размещения хранит число элементов для каждого размещаемого       массива. Требование использовать для удаления массивов только операцию       delete[] освобождает функцию размещения от обязанности хранить счетчики       числа элементов для каждого массива. Исполнение такой обязанности в       реализациях С++ вызывало бы существенные потери времени и памяти       и нарушило совместимость с С.

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.