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



бет45/124
Дата16.07.2016
өлшемі3.27 Mb.
#204081
түріКнига
1   ...   41   42   43   44   45   46   47   48   ...   124

5.5.3 Свободная память


Рассмотрим пример:


main()

{

table* p = new table(100);



table* q = new table(200);

delete p;

delete p; // вероятно, вызовет ошибку при выполнении

}
Конструктор table::table() будет вызываться дважды, как и деструктор

table::~table(). Но это ничего не значит, т.к. в С++ не

гарантируется, что деструктор будет вызываться только для объекта,

созданного операцией new. В этом примере q не уничтожается вообще,

зато p уничтожается дважды! В зависимости от типа p и q программист

может считать или не считать это ошибкой. То, что объект не

удаляется, обычно бывает не ошибкой, а просто потерей памяти. В то же

время повторное удаление p - серьезная ошибка. Повторное применение

delete к тому же самому указателю может привести к бесконечному

циклу в подпрограмме, управляющей свободной памятью. Но в языке

результат повторного удаления не определен, и он зависит от

реализации.

Пользователь может определить свою реализацию операций new и

delete (см. $$3.2.6 и $$6.7). Кроме того, можно установить

взаимодействие конструктора или деструктора с операциями new и

delete (см. $$5.5.6 и $$6.7.2). Размещение массивов в свободной

памяти обсуждается в $$5.5.5.



5.5.4 Объекты класса как члены


Рассмотрим пример:


class classdef {

table members;

int no_of_members;

// ...


classdef(int size);

~classdef();

};
Цель этого определения, очевидно, в том, чтобы classdef содержал

член, являющийся таблицей размером size, но есть сложность: надо

обеспечить вызов конструктора table::table() с параметром size. Это

можно сделать, например, так:


classdef::classdef(int size)

:members(size)

{

no_of_members = size;



// ...

}

Параметр для конструктора члена (т.е. для table::table()) указывается



в определении (но не в описании) конструктора класса, содержащего

член (т.е. в определении classdef::classdef()). Конструктор для

члена будет вызываться до выполнения тела того конструктора, который

задает для него список параметров.

Аналогично можно задать параметры для конструкторов других членов

(если есть еще другие члены):


class classdef {

table members;

table friends;

int no_of_members;

// ...

classdef(int size);



~classdef();

};
Списки параметров для членов отделяются друг от друга запятыми (а не

двоеточиями), а список инициализаторов для членов можно задавать в

произвольном порядке:


classdef::classdef(int size)

: friends(size), members(size), no_of_members(size)

{

// ...


}
Конструкторы вызываются в том порядке, в котором они заданы в

описании класса.

Подобные описания конструкторов существенны для типов,

инициализация и присваивание которых отличны друг от друга, иными

словами, для объектов, являющихся членами класса с конструктором,

для постоянных членов или для членов типа ссылки. Однако, как

показывает член no_of_members из приведенного примера, такие

описания конструкторов можно использовать для членов любого

типа.

Если конструктору члена не требуется параметров, то и не нужно



задавать никаких списков параметров. Так, поскольку конструктор

table::table() был определен со стандартным значением параметра,

равным 15, достаточно такого определения:
classdef::classdef(int size)

: members(size), no_of_members(size)

{

// ...


}
Тогда размер таблицы friends будет равен 15.

Если уничтожается объект класса, который сам содержит объекты

класса (например, classdef), то вначале выполняется тело

деструктора объемлющего класса, а затем деструкторы членов в порядке,

обратном их описанию.

Рассмотрим вместо вхождения объектов класса в качестве членов

традиционное альтернативное ему решение: иметь в классе указатели

на члены и инициализировать члены в конструкторе:


class classdef {

table* members;

table* friends;

int no_of_members;

// ...

};
classdef::classdef(int size)



{

members = new table(size);

friends = new table; // используется стандартный

// размер table

no_of_members = size;

// ...


}
Поскольку таблицы создавались с помощью операции new, они должны

уничтожаться операцией delete:


classdef::~classdef()

{

// ...



delete members;

delete friends;

}
Такие отдельно создаваемые объекты могут оказаться полезными, но

учтите, что members и friends указывают на независимые от них

объекты, каждый из которых надо явно размещать и удалять. Кроме

того, указатель и объект в свободной памяти суммарно занимают

больше места, чем объект-член.

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[] освобождает функцию размещения от обязанности хранить счетчики

числа элементов для каждого массива. Исполнение такой обязанности в

реализациях С++ вызывало бы существенные потери времени и памяти

и нарушило совместимость с С.



Достарыңызбен бөлісу:
1   ...   41   42   43   44   45   46   47   48   ...   124




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

    Басты бет