Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
111
Добавлен:
02.05.2014
Размер:
94.21 Кб
Скачать

X(…) описываем все необходимые действия при инициализации

~X(…) описываем все необходимые действия при освобождение объекта

};

Таким образом в С++ реализована очень удобная концепция работы с ресурсами. Если ресурсы нам требуются временно, тогда мы просто описываем какой-то программный блок, в котором есть объект, имеющий конструктор, захватывающий нужный ресурс, и деструктор, его освобождающий. Наиболее характерный пример в данной ситуации – это выполнение длинной операции. Допустим, что мы заказали обмен с сетевым драйвером (эта операция в ряде случаев может достаточно на долго затянуться). Правила хорошего тона требуют от программиста как-то предупредить пользователя, что операция может затянуться, что бы последний не дергался. Чаще всего курсор превращается в песочные часы (интересно то, что им двигать достаточно не удобно – это тебе не стрелочка-указатель). Почти во всех графических библиотеках есть класс, в котором конструктор и деструктор реализованы как-то так:

CHourGlass( ) {

m_old Cursor = Load Cursor :: CHourGlass; }

~ChourGlass( ) { LoadCurssor = m_odl Cursor };

Такой код работает очень хорошо. Правда от меня еще требуется проверки, а загрузился ли вообще курсор, но это уже превращение просто программы в хорошую программу.

{ChourGlass cur;

…………………. Выполняем какие-то действия

};

Даже если у нас произойдет какая- то беда и выход буден ненормальным,

курсор восстановится автоматически.

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

Class Vector {

Int * body;

Int size;

Vector (int S) { … }

};

Конструктор должен содержать параметр, определяющий соответствующую длину. Каким образом компилятор должен догадаться, какой параметр поставить? Есть понятие «конструктор по умолчанию», т.е. непараметрический конструктор (конструктор без параметров). Если нет явного указания, какой конструктор вызвать, компилятор вызывает конструктор умолчания.

Например, если написать что-то типа х( );, то будет поставлен конструктор умолчания, хотя в данном случае скобки лучше опустить и написать: Т х; Компилятор сам знает, что сюда вставить. В случае, когда у нас отсутствует конструктор умолчания, как в примере Vector (какое число тут можно взять по умолчанию? Никакое число не будет здесь лучше другого), следовательно, никакого конструктора умолчания у объектов данного класса быть не должно. Мы должны иметь возможность явным образом указывать соответствующие параметры.

Vector v(10);

Vector *pv = new Vector(256);

Тут у нас уже никаких проблем нет, т.е. появляется несколько видов конструкторов: конструктор умолчания, конструктор с параметрами. Есть еще некоторая особенность языка С++, которая несколько усложняет работу с конструкторами, а, следовательно, и с объектами, поскольку каждый объект имеет конструктор. Иногда это удобно, но могут возникать проблемы из-за того, что конструктор в С++ имеет еще и некоторую системную семантику. Довольно часто можно увидеть конструктор, у которого собственное тело пустое. Это возникает от того, что кроме семантики, которую задает программист (пользовательской семантики), есть еще некоторая системная семантика, т.е. компилятор вставляет некоторые свои действия. Например, при наследовании. Пусть у нас есть иерархия классов: класс Т2 унаследован от класса Т1. При этом конструктор класса Т2 будет содержать в себе обращение к конструктору базового класса Т1. Сначала будет вызван при инициализации конструктор Т1, а потом по цепочки наследования будет вызван конструктор класса Т2. Компилятор это делает автоматически, причем сверху вниз по цепочке наследования. Точно такая же ситуация и с деструктором, который в себе содержит некоторую системную часть. Сначала вызываются деструкторы производных классов, а по цепочке до базового. У конструктора сначала работа конструктора класса (здесь тело может быть пустое, т.е. пользовательская семантика), а потом конструктора объекта. У деструктора все в обратном порядке. Сначала вызывается деструктор пользовательского объекта, а потом вверх по цепочке наследования до базового класса. Отдельно рассмотреть надо случай, если конструктор содержит подобъекты. Любой класс в С++ содержит конструктор, хотя бы один, и содержит деструктор, тоже хотя бы один. Даже если мы не описываем его явно, код все равно будет сгенерирован автоматически, и тело будет непустое в случае, когда есть отношение наследования и отношение включения.

Пусть есть класс Х:

Class X {

……..

};

Рассмотрим теперь класс Y, который содержит подобъект класса Х. Спрашивается, какой и когда конструктор будет вызван?

Class Y {

X x;

};

Сначала вызывается конструктор базового класса, потом конструктор подобъектов, а уж потом конструктор объектов. Компилятор будет автоматически, когда он генерирует новый конструктор для класса Y, вызывать конструктор умолчания для подобъекта класса Х. А если конструктора умолчания нет, как в примере с классам Vector? То в этом случае используется несколько расширенная семантика конструктора.

X (параметры) : конструкторы подобъектов.

Или

Y( ) : V(28) {…}; Здесь мы обязательно должны явным образом указать конструктор, потому что нет конструктора умолчания.

Все вышесказанное распространяется и на базовый класс.

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

Class Y {

int &i

};

Y( int a ) : i(a){…};

Из синтаксиса хорошо видно, что сначала происходит вызов конструктора базового класса, потом объекта, а потом уже пользовательская часть. Надо учитывать то, что если у нас много подобъектов, и мы соответствующие параметры перечислили чрез запятую, компилятор оставляет за собой право делать вызовы в такой последовательности, в которой ему удобно. Программист никогда не должен полагаться на то, что он знает в какой последовательности компилятор, что вызовет.

В языке С++ есть дополнительные навороты, которые вызывают дополнительные трудности. Пока мы с Вами видели конструкторы умолчания и конструкторы параметрические, причем конструктор умолчания вызывается только тогда, когда нет вызова параметрического конструктора. Есть конструкторы явные, которые пишутся самим программистом, и есть конструкторы неявные, которые компилятор генерирует, если в классе явным образом не описан конструктор.

Есть еще одна проблема, которая уже связана с копированием.

Соседние файлы в папке Лекции по программированию на ЯВУ