Программирование >>  Поддержка объектно-ориентированного программирования 

1 ... 42 43 44 [ 45 ] 46 47 48 ... 120


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();

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

1 44



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

classdef::classdef(int size)

: friends(size), members(size), no of members(size)

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

Подобные описания конструкторов существенны для типов, инициализация и присваивание которых отличны друг от друга, иными словами, для объектов, являющихся членами класса с конструктором, для постоянных членов или для членов типа ссылки. Однако, как показывает член no of members из приведенного примера, такие описания конструкторов можно использовать для членов любого типа.

Если конструктору члена не требуется параметров, то и не нужно задавать никаких списков параметров. Так, поскольку конструктор table::table() был определен со стандартным значением параметра, равным 1 5, достаточно такого определения:

classdef::classdef(int size)

: members(size), no of members(size)

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

Если уничтожается объект класса, который сам содержит объекты класса (например, 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];

будет создан массив из 1 0 таблиц, каждая из которых инициализируется вызовом table::table(1 5), поскольку вызов table::table() будет происходить с фактическим параметром 1 5.

В описании массива объектов не предусмотрено возможности указать параметры для конструктора. Если члены массива обязательно надо инициализировать разными значениями, то начинаются трюки с глобальными или статическими членами.

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

void f()

table* t1 table* t2 delete t1; delete t2;

new table; new table[10];

таблица

удаляется одна неприятность: на самом деле удаляется 10

таблиц

В данном случае программист должен указать, что t2 - указатель на массив:

new table; new table[sz];

void g(int sz)

table* t1 = table* t2 = 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 = 12 8 };



1 ... 42 43 44 [ 45 ] 46 47 48 ... 120

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика