Конструкторы и деструкторы
При создании экземпляров классов очень часто (практически всегда) требуется выполнять определенные, инициализирующие эти экземпляры, действия с членами данных. В нашем примере класса такими действиями являются выделение в динамической области необходимого для массива объема памяти и формирование соответствующих значений членов класса Arr и ItemCount.
Локальные переменные, соответствующие экземплярам класса, существуют только внутри блока, в котором они определены. При закрытии блока эти переменные уничтожаются, и доступ к соответствующим экземплярам становится невозможен. Но если, например, как в нашем случае, при создании экземпляра класса был захвачен некоторый объем динамической памяти, то перед тем как уничтожить экземпляр класса, необходимо освободить захваченную динамическую память, иначе произойдет «утечка» памяти. То есть при уничтожении экземпляров классов очень часто также необходимо выполнить определенные операции. Подобные операции мы выполняли с помощью функции Freeнашего класса.
Однако, сторонний программист, использующий наш класс, может не знать, что при создании экземпляров нашего класса каждый экземпляр «захватывает» динамическую память и что эту память надо своевременно освобождать. Он может не знать об обязательности использования функции Free перед уничтожением каждого экземпляра. В результате динамическая область может быть переполнена, и программа перестанет работать.
Для упрощения использования классов при создании и уничтожении экземпляров класса рекомендуется использовать специальные конструкторы и деструкторы.
Конструкторы и деструкторы представляют собой специальные функции члены, которые вызываются автоматически (без участия программиста). При создании экземпляра вызывается конструктор, а при уничтожении – деструктор.
Конструктор представляет собой функцию, не возвращающую никаких значений, имя которой совпадает с именем класса. Конструктор может быть с параметрами или без них. Например, конструктор для нашего класса можно определить так:
t_DinArr (unsigned N = 0);
Это прототип конструктора с параметром, определяющим количество элементов динамического массива.
Прототип конструктора помещается в раздел открытых членов класса. Его реализация может быть выполнена или раздельно или встроена в само определение класса. Реализация конструктора для нашего класса при раздельном определении выглядит так (имеет префикс t_DinArr ::):
t_DinArr :: t_DinArr (unsigned N = 0)
{
Init ( N );
}
При встроенной реализации конструктора он будет таким (без префикса t_DinArr ::):
t_DinArr (unsigned N = 0) { Init ( N ); }
или таким:
t_DinArr (unsigned N = 0)
{
Init ( N );
}
Конструктор в нашем случае просто вызывает функцию Initдля инициализации экземпляра на заданное количество элементов массива.
Деструктор это функция, также не возвращающая значений, но и не имеющая параметров. Его имя также совпадает с именем класса, но впереди используется признак деструктора – символ ~:
~ t_DinArr ( );
Это прототип деструктора, помещаемый в описание класса при раздельной реализации деструктора, которая выглядит так:
t_DinArr :: ~ t_DinArr ( )
{
Free ( );
}
Реализацию деструктора также можно поместить внутрь описания класса:
~ t_DinArr ( ) { Free ( ); }
Деструктор автоматически вызывается при уничтожении экземпляра и автоматически освобождает динамическую память с помощью функции Free.
Таким образом, при использовании конструктора и деструктора наш класс будет выглядеть так:
class t_DinArr {
// Закрытые члены-данные класса
t_Inf *Arr;// Указатель на динамический массив
unsigned ItemCount;// Количество элементов в массиве
public:
// Открытые члены-функции класса
t_DinArr (unsigned N = 0) { Init ( N ); } //Конструктор
~ t_DinArr ( ) { Free ( ); } //Деструктор
………..// Далее, как и раньше, остальные члены функции класса
};
Теперь в программе для создания экземпляров класса можно использовать конструктор. В зависимости от вида конструктора (имеет он параметры или нет) его можно использовать по-разному.
Пусть имеется некоторый блок программы, в котором нам необходимо использовать динамические массивы.
{
t_DinArr DA1 ( 20 ); //1-й способ
t_DinArr DA2 = 10; //2-й способ
t_DinArr DA3; //3-й способ
t_DinArr DA4 = t_DinArr ( 30); //4-й способ
…..
// Работаем с экземплярами
…..
}
В этом примере показаны 4 способа применения конструктора класса при создании экземпляров класса t_DinArr.
Способ 1 – наиболее распространенный. В нашем случае конструктор имеет один параметр, его-то мы и используем при создании экземпляра DA1. В результате DA1 будет представлять собой массив из 20 элементов типа t_Inf. Если конструктор имеет несколько параметров, то при создании экземпляра, необходимо внутри скобок перечислить через «запятую» все значения этих параметров. Например, если конструктор некоторого класса Same_Class имеет 3 целочисленных параметра, то экземпляр такого объекта следует создавать так: Same_Class SC ( 1, 2, 3 );.
Способ 2 можно использовать только в случае, когда конструктор имеет 1 параметр. В классе t_DinArrиспользуется именно такой конструктор. В результате DA2 будет представлять собой массив из 10 элементов типа t_Inf. Если у конструктора несколько параметров, то такой способ не применим.
Способ 3 используется, если у конструктора нет параметров, или все параметры имеют значения по умолчанию. Этот способ можно применить к классу t_DinArr, так как его конструктор хоти и имеет один параметр, но этот параметр имеет значение по умолчанию, равное 0. В результате DA3 будет представлять собой массив из 0 элементов, то есть динамический массив пуст.
Способ 4 используется достаточно редко из-за своей избыточности в записи. В результате DA4 будет представлять собой массив из 30 элементов типа t_Inf.
Все переменные DA1, DA2, DA3, DA4 являются локальными по отношению к блоку, в котором они созданы. При закрытии блока все они будут уничтожены. При уничтожении каждой из них будет автоматически вызван деструктор соответствующего экземпляра, который освободит динамическую память, выделенную для каждого динамического массива. Специально вызывать для этого функции Free для каждого экземпляра не надо.
Текст программы, реализующей пример использования класса t_DinArr с использованием встроенных функций членов, конструктора и деструктора, приведен в Приложении 2.
Теперь несколько замечаний об использовании конструкторов при инициализации массивов экземпляров объектов. О работе с такими массивами мы говорили в параграфе 13.3. В этом параграфе был рассмотрен пример создания массива из 6 экземпляров класса t_DinArr.
Статический массив из 6 экземпляров классаt_DinArr:
t_DinArr A [ 6 ];
В параграфе 13.3 инициализацию экземпляров в этом массиве мы делали с помощью цикла:
for ( int i = 0; i < 6; ++ i )
A [ i ].Init ( i + 1 );
В результате мы получили массив на 6 экземпляров класса t_DinArr, в котором каждый экземпляр представлял собой динамический массив с количеством элементов, равным номеру экземпляра (получилась некоторая треугольная структура). Этот вариант инициализации можно использовать и при наличии конструктора класса. Однако наличие конструктора дает более простую возможность инициализации статического массива A:
t_DinArr A [ 6 ] = { 1, 2, 3, 4, 5, 6 };
Такая инициализация возможна, если конструктор класса имеет один параметр (у нас так и есть).
Можно использовать более длинную, но более универсальную форму такой инициализации, которая сможет работать с конструкторами, имеющими любое количество параметров:
t_DinArr A [ 6 ] = { t_DinArr ( 1 ), t_DinArr ( 2 ), t_DinArr ( 3 ),
t_DinArr ( 4 ), t_DinArr ( 5 ), t_DinArr ( 6 ) };
Здесь конструкторы вызываются в явном виде и им можно передавать столько параметров, сколько в них определено.
Дата добавления: 2019-02-07; просмотров: 335;