Малышев Сергей Михайлович
Речьпойдет о четырех очень специальных членах, которые должны
учитываться присоздании каждого класса. Это конструктор по
умолчанию, конструктор копий, операцияприсваивания и деструктор.
Заметьте, это вовсе не значит, что вы должныобязательно каждый раз
писать все эти четыре функции для каждого класса, которыйвы
создаете. Речь идет только о том, что вы должны принимать их во
внимание присоздании любого класса и делать выводы об их
необходимости.
Этичетыре члена в принципе являются функциями, но не типичными. Они
выглядятсовсем как функции, но, как несложно заметить, некоторые из
них не возвращаютникаких значений. Эти функции предназначены для
создания (инициализации), копированияи удаления (разрушения)
объектов класса.
BR>Очень подробно мы рассмотрим проблемы копирования и
присваивания для классов, посвятивэтому несколько отдельных
материалов. А пока, все, что излагается ниже, можнорассматривать,
как вводную часть к этим довольно сложным вопросам.
1. Конструкторы
Конструкторы- это члены классов, используемые для создания
объектов-экземпляров классов.Есть несколько разновидностей
конструкторов, в их числе есть довольносвоеобразные. Но основное их
назначение в любом случае одно и то же:обеспечение удобного способа
создания объекта-экземпляра класса.
Мырассмотрим конструктор по умолчанию, конструктор копий, аргументы
по умолчаниюв конструкторе и другие конструкторы.
Какуже было отмечено, конструкторы — это функции-члены, не
возвращающие никакихзначений (даже типа void). Другой их
особенностью является то, что их имядолжно в точности, включая
регистр символов, совпадать с именем класса. То есть,если класс
называется Any_Class, то его конструктор также должен
называтьсяAny_Class.
2. Конструктор по умолчанию.
Итак,конструктор по умолчанию (default constructor) — это
конструктор, непринимающий (не имеющий) аргументов.
Такимобразом, конструктор по умолчанию для некоего произвольного
класса будетвыглядеть так:
class ANY_CLASS
{
public:
ANY_CLASS(); //конструктор по умолчанию
…//тут все остальное
};
Обычноконструкторы объявляются в открытой (public) секции класса,
посколькудеятельность конструкторов заключается в создании объекта
типа класса, и онивызываются извне класса. Вызовы конструкторов,
как правило, происходят неявно.Например, создание одиночного
объекта типа ANY_CLASS может выглядеть следующимбразом:
ANY_CLASSас; // ас — это объект класса ANY_CLASS
Заметьте,что в этом операторе совершенно отсутствуют скобки,
конструирование — этонеявная операция.
Массивобъектов типа ANY_CLASS может быть создан так:
ANY_CLASSaac[10]; // aас — это массив из 10 элементов
Каквидите, синтаксис объявления массива объектов точно такой же,
как и синтаксисобъявления статического массива данных любого
базового (встроенного) типа. Однаиз задач языка C++ состоит в
предоставлении пользователям возможностиобращаться со сложными
типами данных таким же образом, как и со встроенными.
Благодарянеявной природе конструирования объектов достигается
первый ее аспект: созданиеобъекта выглядит точно так же, как и
создание обычной переменной.
Кстати,создать МАССИВ объектов можно ТОЛЬКО в том случае, если для
класса определенконструктор по умолчанию.
3.Конструктор копий.
Работаконструктора копий (copy constructor) заключается в
предоставлении возможностиинициализации (создания) нового объекта
из уже существующего. Для объясненияглубинных механизмов этого
процесса потребовалась бы не одна глава, поэтому мыознакомимся с
ними пока вкратце.
Общийсинтаксис конструктора копий таков:
My_Class(const My_Class&); // здесьMy_Class — это имя
класса
Вконструкторе копирования класса My_Class в качестве параметра
используетсяссылка на объект этого класса. Причём эта ссылка
объявляется со спецификаторомconst. И в этом нет ничего
странного.
Какизвестно, выражение вызова функции с параметром типа X ничем не
отличается отвыражения вызова функции, у которой параметром
является ссылка на объект типаX. При вызове такой функции не
приходится копировать объекты как параметры.Передача адреса не
требует копирования объекта, а значит, при этом не будет
ирекурсии.
Внутрьтела конструктора копий обычно помещается серия присваиваний,
посредствомкоторых каждому элементу объекта-аргумента присваивается
значениесоответствующего элемента вызывающего объекта.
Здесьесть много моментов, которые следует иметь в виду, и все они
исключительноважны для понимания классов C++, но дальнейшее
обсуждение конструктора копий мыотложим до главы, специально
посвященной копированию и присваиванию.
4.Аргументы по умолчанию в конструкторе.
Улюбых функций могут быть аргументы по умолчанию. Это относится к
конструкторамв той же степени, как и к любым остальным
функциям-членам и глобальнымфункциям. Аргумент по умолчанию — это
значение, которое присваивается аргументу,если пользователь явно не
задал иное значение.
Посколькусейчас речь идет конкретно о конструкторах, рассмотрим
соответствующий пример.Аргументы по умолчанию удобны в тех случаях,
когда известно определенное (илипредпочтительное) значение
аргумента, но при этом желательно сохранитьвозможность задания
другого значения при создании объекта. Рассмотрим вкачестве примера
некий гипотетический класс, описывающий файл.
classFILE
{
public:
FILE(char *FileName = «file.bin»);
//«file.bin» — это аргумент по
//умолчанию… //все остальное
};
Еслиаргументу FileName типа char * в конструкторе не передать какое
либо значение, тобудет автоматически подставлено значение
«file.bin».
Такимобразом, экземпляры класса FILE можно создавать следующими
способами:
FILEIniFile; //будет создан файл с именем file.bin
FILEArchive(«Archive.dat»); //будет создан файл с именем
Archive.dat
Правилоиспользования аргументов по умолчанию таково: аргументу
можно задать значениепо умолчанию, если он находится правее всех в
списке аргументов, или если всеаргументы правее него имеют значения
по умолчанию.
Нижеприведены несколько фиктивных конструкторов, демонстрирующих
примерыправильного и неправильного употребления аргументов по
умолчанию:
DATA(intа=0, int b); // явная ошибка: DATA F(, 5) смотрится
глупо…
DATA(intа, int b=10); // правильно, можно создать объекты DATA
G(5);
//или DATA G(3, 4);
DATA(inta=0, int b=10);// правильно, можно создать объекты DATA
Н(3, 4);
//или DATA R;
Правилодля аргументов по умолчанию было введено для того, чтобы не
возникало ситуацийтипа «пробел запятая аргумент» (см. первый пример
для объекта F(, 5)),которые весьма чреваты ошибками, да и выглядят
неважно.
Необходимотакже отметить следующее: конструктор, все аргументы
которого снабженызначениями по умолчанию, может вызываться и с
аргументами, и без аргументов, тоесть при вызове выглядеть как
обычный конструктор по умолчанию (см. пример дляDATA Н(3, 4); и
DATA R;).
Поэтомужелательно избегать неопределенности, возникающей при
одновременном задании вклассе конструктора по умолчанию, то есть
без аргументов, и конструктора, укоторого все аргументы имеют
значения по умолчанию.
5.Конструкторы в целом.
Итак,небольшое заключение. Конструкторы предназначены для того,
чтобы обеспечитьразработчика класса средством для удобной
инициализации каждой из составныхчастей класса. Количеству и
разнообразию аргументов конструктора нет предела.Однако, обычно
типы аргументов должны соответствовать данным-членам класса, тоесть
тому, что в конце концов надо инициализировать.
Требованийк созданию конструкторов немного: вы вынуждены учитывать,
что конструкторвызывается неявно, что он не имеет возвращаемого
значения и что его имя вточности должно совпадать с именем его
класса.
6.Деструкторы
Деструкторывыполняют работу, обратную той, что проделывают
конструкторы. Хотя класс можетиметь несколько конструкторов, но
деструктор может быть только один. Синтаксисдеструктора очень похож
на синтаксис конструктора по умолчанию. Точно такжедеструктор не
имеет аргументов, все различие заключается в том, что
деструктор,будучи по своей сути функцией, парной конструктору,
имеет то же имя, что икласс, но с приставкой в виде операции
дополнения (~).
Тоесть деструктор любого класса имеет такую форму:
class ANY_CLASS
{
public:
ANY_CLASS(); //конструктор по умолчанию
ANY_CLASS(intd); //еще один конструктор
~ANY_CLASS();//а это — деструктор
};
Деструкторпочти всегда вызывается неявно. Вызов деструктора
происходит либо при выходеобъекта за пределы своей области
видимости, либо при уничтожении динамическогосозданного (операцией
new) объекта операцией delete.
7.Виртуальный деструктор.
Есликласс может иметь наследников, то предпочтительнее использовать
виртуальныйдеструктор. Синтаксис виртуального деструктора точно
такой же, как и у любогодругого деструктора, за исключением того,
что его объявление начинается сключевого слова virtual.
class ANY_CLASS
{
public:
ANY_CLASS(); //конструктор по умолчанию
vrtual~ANY_CLASS(); //а это — виртуальнный деструктор
};
Объявлениедеструктора виртуальным не отразится на
производительности. Так что имеет смыслвсегда делать его
виртуальным, если нет очень веских причин воздержаться отэтого.
Отказот применения виртуальных деструкторов может привести к
утечкам памяти впроизводных классах, но это тема для отдельного
разговора. К ней мы вернемсяболее подробно позднее.
Приведенныйниже пример придуман исклю
include«iostream.h»
//Этот класс просто демонстрирует неявные вызовы
//конструктора и деструктора
classDEMO
{
public:
DEMO() { cout " «constructor» " endl; }
virtual ~DEMO() { cout " «destructor» " endl; }
};
voidmain(void)
{
DEMOstaticDemo; // статическое размещение, деструктор вызывается
при
//выходе за пределы области видимости
DEMO*dynamicDemo = new DEMO;
//динамическое размещение,
//деструктор вызывается при уничтожении объекта
deletedynamicDemo;
}
Вэтом примере определяется класс, не содержащий ничего, кроме
открытых (public)деструктора и конструктора. Обе этих функции
объявлены и определены в классе.При создании объекта неявно
вызывается конструктор и печатается слово«constructor», а при
вызове деструктора, соответственно, слово«destructor».
Внутрифункции main() создаются два объекта, один статический, в
стеке, а второй вкуче (heap) — динамический. В результате
выполнения этого примера на экранбудет выведено следующее:
constructor
constructor
destructor
destructor
Перваястрока выводится конструктором при создании объекта
staticDemo.
Втораястрока выводится конструктором при создании объекта
dynamicDemo.
Третьястрока — результат вызова деструктора объекта dynamicDemo при
его уничтоженииоператором delete.
Четвертаястрока — результат вызова деструктора объекта
staticDemo.
Деструкторстатического объекта был вызван последним, при выходе
объекта из областивидимости — в той точке, где расположена
закрывающая скобка функции main().
8.Операция присваивания.
Задачаоперации присваивания для класса состоит в том, чтобы дать
вам возможностьсделать один объект эквивалентным другому. Сама
операция представляет из себязнак равенства (=). Присваивание
настолько важно, что если вы сами нереализуете его, компилятор
сделает это за вас.
Вспомните:мы уже говорили о том, что для каждого создаваемого
класса вы должны приниматьво внимание конструктор по умолчанию,
конструктор копий, операцию присваиванияи деструктор. Иногда имеет
смысл воспользоваться предопределеннымиконструктором копий и
операцией присваивания — то есть теми их версиями,
которыеавтоматически генерируются компилятором.
Ксожалению, это удается не всегда. Возможны ситуации, когда
инициализация иликопирование новых объектов из других объектов
просто недопустимы; для указанияэтого существуют специальные
способы. Пока что, до выяснения подробностей, мывоздержимся от
рассмотрения примеров неправильного применения функций копированияи
присваивания.
9.Указатель на самого себя: this
Напоследокрассмотрим еще одно понятие — указатель на самого себя,
поскольку в дальнейшемматериале мы будем использовать это
понятие.
Укаждого объекта есть физическое местоположение в памяти (адрес).
Оперировать сэтими местоположениями можно посредством указателей.
Как правило, в указателяххранятся адреса. Обращаясь к указателю, мы
в действительности обращаемся кобъекту, расположенному по
содержащемуся в этом указателе адресу.
Каждыйобъект класса имеет свой физический адрес и его можно извлечь
из указателяthis. Это внутренний (как бы скрытый от «прямого
взгляда») указатель,который есть у каждого класса. На самом деле мы
всегда неявно используемуказатель this, когда обращаемся к членам
внутри области видимостифункции-члена класса. Вот пример:
сlassTHIS_DEMO
{
public:
THIS_DEMO(){this->a = 5; } //обычно мы просто пишем а=5;
…//все остальное
private:
inta;
};
Имеетсякласс, в котором описан конструктор и член класса int a в
закрытой секции(private) класса. В конструкторе THIS_DEMO
содержится присваиваниецелочисленному члену а значения 5. Обычно
this явно не указывается, простопишут а = 5;. Но в данном случае мы
демонстрируем, то, что всегда неявнопроисходит — использование this
внутри функций-членов класса при обращении кчленам класса.
Конечно,нет надобности использовать этот указатель для обращения к
членам классаизнутри класса, но это единственное средство сослаться
из объекта на объект вцелом. Указатель this иногда бывает крайне
полезен.
Нувот, на этот раз все.
Следующийматериал будет разбит на несколько частей и посвящен
проблемам копирования иприсваивания в классах.
Есливам интересно, или возникают вопросы — пишите, разберемся.
Список литературы
P.Kimmel Using Borland C++ 5 Special Edition переводBHV —
С.Петербург 1997
Дляподготовки данной работы были использованы материалы с сайта
my-pc.jino.ru/
Элементы класса, о которых всегда необходимо помнить
112
0
7 минут
Понравилась работу? Лайкни ее и оставь свой комментарий!
Для автора это очень важно, это стимулирует его на новое творчество!