Бьерн Страуструп. Язык программирования С++ Второе дополненное издание



Pdf көрінісі
бет113/256
Дата11.07.2022
өлшемі2,87 Mb.
#37591
1   ...   109   110   111   112   113   114   115   116   ...   256
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 }; 


Бьерн Страуструп.
Язык программирования С++ 
 
147 
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
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. 


Достарыңызбен бөлісу:
1   ...   109   110   111   112   113   114   115   116   ...   256




©emirsaba.org 2024
әкімшілігінің қараңыз

    Басты бет