Свободная Память
Рассмотрим:
main() { table* p = new table(100); table* q = new table(200); delete p; delete p; // возможно, ошибка }
Конструктор table::table() будет вызван дважды, как и деструктор table::~table(). То, что C++ не дает никаких гарантий, что для объекта, созданного с помощью new, когда-либо будет вызван деструктор, ничего не значит. В предыдущей программе q не уничтожается, а p уничтожается дважды! Программист может счесть это ошибкой, а может и не счесть, в зависимости от типа p и q. Обычно то, что объект не уничтожается, является не ошибкой, а просто лишней тратой памяти. Уничтожение p дважды будет , как правило, серьезной ошибкой. Обычно результатом применения delete дважды к одному указателю приводит к бесконечному циклу в подпрограмме управления свободной памятью, но определение языка не задает поведение в таком случае, и оно зависит от реализации.
Пользователь может определить новую реализацию операций new и delete (см. этот пункт). Можно также определить способ взаимодействия конструктора или деструктора с операциями new и delete (см. этот раздел)
Объекты Класса и Члены
Рассмотрим
class classdef { table members; int no_of_members; // ... classdef(int size); ~classdef(); };
Очевидное намерение состоит в том, что classdef должен содержать таблицу длиной size из членов member, а сложность - в том, как сделать так, чтобы конструктор table::table() вызывался с параметром size. Это делается примерно так:
classdef::classdef(int size) : members(size) { no_of_members = size; // ... }
Параметры для конструктора члена member (здесь это 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; // ... }
Порядок, в котором вызываются конструкторы, не определен, поэтому не рекомендуется делать списки параметров с побочными эффектами:
classdef::classdef(int size) : friends(size=size/2), members(size); // дурной стиль { no_of_members = size; // ... }
Если конструктору для члена не нужно ни одного параметра, то никакого списка параметров задавать не надо. Например, поскольку table::table был определен с параметром по умолчанию 15, следующая запись является правильной:
classdef::classdef(int size) : members(size) { no_of_members = size; // ... }
и размер size таблицы friend"ов будет равен 15.
Когда объект класса, содержащий объект класса, (например, classdef) уничтожается, первым выполняется тело собственного деструктора объекта, а затем выполняются деструкторы членов.
Рассмотрим традиционную альтернативу тому, чтобы иметь объекты класса как члены, - иметь члены указатели и инициализировать их в конструкторе:
class classdef { table* members; table* friends; int no_of_members; // ... classdef(int size); ~classdef(); };
classdef::classdef(int size) { members = new table(size); friends = new table; // размер таблицы по умолчанию no_of_members = size; // ... }
Так как таблицы создавались с помощью new, они должны уничтожаться с помощью delete:
classdef::~classdef() { // ... delete members; delete friends; }
Раздельно создаваемые объекты вроде этих могут оказаться полезными, но учтите, что members и friends указывают на отдельные объекты, что требует для каждого из них действие по выделению памяти и ее освобождению. Кроме того, указатель плюс объект в свободной памяти занимают больше места, чем объект член.
1 2 3 4
8 8 8
|