Использование конструкторов и деструкторов 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Использование конструкторов и деструкторов



(2 часа)

Цель работы: изучить и научиться использовать механизм работы с конструкторами и деструкторами.

Теоретические сведения

Конструкторы и деструкторы

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

1 Они имеют имя, совпадающее с именем класса ( деструкторы с символом тильды ~)

2 Конструкторов может быть несколько, деструктор только один.

3 Они не имеют объявлений типа возвращаемых значений (даже void).

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

5 Конструкторы, как и большинство функций языка С, могут иметь аргументы по умолчанию или использовать списки инициализации элементов.

6 Деструкторы могут иметь атрибут virtual, но конструкторы не могут.

7 Нельзя работать с адресами этих функций.

Пример

void *ptr=base::base; // недопустимо, base()– функция-конструктор класса base

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

Примеры

X *p; // указатель на класс Х

X x; // объявление объекта х класса Х

p=&x; // указатель p хранит адрес объекта х

p->X::~X(); // допустимый вызов деструктора

X::X(); // недопустимый вызов конструктора

 

9 При объявлении объектов компилятор вызывает один из конструкторов автоматически.

10 Аналогично при уничтожении объектов компилятор автоматически вызывает деструктор.

11 Конструкторы и деструкторы при необходимости динамического выделения объекту памяти могут выполнять вызовы операторов new и delete.

12 Конструктор создает объект х и инициализирует его данные-члены. При копировании объекта данного класса происходит неявный вызов соответствующего конструктора. Данные-члены объекта класса, у которого все элементы общедоступны (public) и без конструкторов или базовых классов (обычно это структура) могут инициализироваться при помощи списка инициализаторов. Деструктор разрушает объект класса, созданный конструктором, когда этот объект выходит из области видимости.

Конструкторы глобальных объектов вызываются до вызова функции main ().

Локальные объекты создаются, как только становится активной область действия (видимости) объекта. При этом конструктор вызывается каждый раз.

Class Х

{...public:

X(); // конструктор класса Х по умолчанию

// X(X); недопустимо

Х(const X&); // конструктор копирования – передаётся ссылка на Х

~X(); // деструктор класса Х

};

Формальные параметры конструктора при его объявлении могут быть любого типа, за исключением класса, элементом которого является данный конструктор. Конструктор копирования принимает в качестве параметра ссылку на свой собственный класс. Конструктор, не воспринимающий параметров, называется конструктором по умолчанию: Х::Х().

Х х; // объявлен объект х класса Х, вызывается конструктор по умолчанию

Как и все функции-члены, конструкторы могут иметь аргументы, используемые по умолчанию. Например, возможны следующие объявления конструкторов в классе:

Объявление класса Определение конструкторов вне класса
class X {int I, jпо- умолчанию private public: X();// конструктор по умолчанию X(int); //перегруженные конструкторы X(int, int);// могут вызываться c одним // X(int) или двумя X(int, int) аргументами X(int а = 5, int в= 6); //может вызываться без аргументов или с одним или двумя // аргументами X(const &); // конструктор копирования }; Х::Х{i=Æ; j=Æ} Х::Х(int a) {i=a; j=Æ;} Х::Х(int a, int b) {i=a; j=b;}

При вызове конструкторов следует избегать неоднозначностей main()

Инициализация объектов класса

Объекты классов с конструкторами могут инициализироваться при помощи задаваемых в круглых скобках списков инициализаторов. Этот список используется как список передаваемых конструктору аргументов

 

Main()

{

X one; // вызывается конструктор X::X() по умолчанию для объекта one

// класса Х

X two(1); // используется конструктор X::X(int)

X three = 1; // вызывается X::X(int) – альтернативная форма со знаком (=)

Х three(10, 8) // использовать нельзя, неоднозначно задан конструктор

// X::X(int а=5, int b=6)

X four = one; // вызывается X::X(const X&) для копирования

// объекта one в four

X five(two); // вызывает X::X(cont X&) для копирования объекта two в five

}

Конструктор воспринимает значения в качестве параметров и выполняет присваивание данным членам в теле функции конструктора. Определение конструктора как и любой функции-члена может производиться в теле класса

 

Class X

{

int i, j;

public:

X(int a, int b) { i = a; j = b } // определение конструктора в классе, он имеет

// доступ к i и j

X(int a, int b): a(i), b(j) {} // альтернативная форма для инициализации а и b

}; i(a), j(b)

В обоих случаях инициализации X x(1, 2) присваивает значение x::i=1 и значение x::j=2.

 

Деструкторы

Деструктор класса вызывается для разрушения элементов объекта до уничтожения самого объекта. Деструктор представляет собой функцию-член, имя которой совпадает с именем класса, перед которым стоит символ тильды (~). Деструктор не может воспринимать каких-либо параметров, а также не объявляет возвращаемого типа или значения.

 

Class X

{…

public:

~X(); // деструктор класса X

};

Если деструктор не объявлен для класса явно, компилятор генерирует его автоматически.

Вызов деструкторов

Вызов деструктора выполняется неявно, когда объект выходит из своей объявленной области действия. Для локальных переменных деструкторы вызываются, когда перестает быть активным блок, в котором они объявлены. В случае глобальных переменных деструкторы вызываются как часть процедуры выхода после функции main(). Необходимость деструкторов в значительной степени связана с указателями и динамическим выделением памяти.

Когда указатели, связанные с объектами, выходят за пределы области действия, неявного вызова деструктора не происходит. Это значит, что для уничтожения такого объекта оператор delete должен задаваться явно.

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

При выходеиз программы с использованием функции exit() деструкторы для каких-либо локальных переменных в текущей области действия не вызываются. Глобальные переменные уничтожаются в обычной последовательности.

При вызове abort () где-либо из программы деструкторы не вызываются даже для переменных глобальной области действия.

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

Примеры:

class X { {... ~X(); ... }; void* operator new(size_t size, void *ptr) { return ptr; } char buffer[sizeof(X)]; main() { X* pointer = new X; // память для // объекта класса Х выделена в куче X* exact_pointer;   exact_pointer = (Х)new(sizeif (X)&buffer) X; // указатель инициализируется адресом // буфера ... delete pointer; // delete служит // для разрушения указателя exact_pointer->X::-X(); // прямой вызов для отмены распределения // памяти }

Ход работы

1 Изучить теоретические сведения.

2 В соответствии с индивидуальным заданием для предыдущей лабораторной работы разработать конструкторы и деструктор для заданного класса. Осуществить инициализацию объектов класса различными конструкторами. Разместить объект класса в динамической памяти и разрушить его после использования в программе.

3 Набрать программу на компьютере и устранить ошибки.

4 Получить результат.

5 Оформить отчет.

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

Требования к содержанию отчёта приведены в лабораторной работе №1.

Контрольные вопросы для подготовки и самостоятельной работы

1 Зачем используются конструкторы и деструкторы?

2 Какое имя имеет конструктор и деструктор?

3 Сколько конструкторов и деструкторов может быть в классе?

4 Можно ли выполнить инициализацию данных-членов без конструктора?

5 Назовите отличия конструкторов и деструкторов от других функций

6 Можно ли явно вызвать конструктор и деструктор?

7 Можно ли передать параметры в конструкторы, использовать параметры по умолчанию?

8 Как определить вызываемый конструктор, если их несколько?

9 Что такое конструктор по умолчанию, конструктор копии?

10 Приведите синтаксис объявления, определения и использования конструкторов, какие альтернативные варианты допустимы?

11 Объясните приведенные примеры.

12 Для чего необходимы оператора new и delete.

13 Когда вызываются деструкторы для локальных и глобальных переменных

14 Как происходит вызов деструкторов при выходе из программы, при вызове функций ехit(), abord()?

Лабораторная работа №22

Использование наследования для создания иерархии классов

(2 часа)

Цель работы: получить навыки в использовании наследования для создания производных классов при простом наследовании.

Теоретические сведения

При объявлении производного класса D перечисляются базовые классы В1, В2... в разделяемом запятой "базовом_списке". Cинтаксис объявления производного класса.

сlass <Имя_класса_D>:<Базовые_классы> {<список_элементов>};

При объявлении класса D перед классами в базовом списке вы можете задать спецификатор доступа public, или private, или protected.

class D: public B1, private B2,... {

...

};

Эти спецификаторы не изменяют доступа к элементам с точки зрения базового класса, но могут изменить доступ к элементам базовых классов из производных классов.

Если D представляет собой объявление класса (class), то по умолчанию используется private, а если D - объявление структуры (struct), то public.

Класс D наследует все элементы базовых классов. (Переопределенные элементы базовых классов наследуются и при необходимости доступ к ним возможен при помощи переопределений области действия). D может использовать элементы базовых классов только с атрибутами public и protected.

Производный класс наследует атрибуты доступа базового класса следующим образом:

1 Если базовый класс указанв списке как public:

- элементы public базового класса становятся элементами public производного класса.

- элементы protected базового класса становятся элементами protected производного класса.

- элементы private базового класса остаются private для производного класса.

2 2. Если базовый класс protected:

- элементы базового класса общедоступные (public) и защищенные (protected) становятся защищенными (protected) элементами производного класса.

- элементы private базового класса остаются для произвольного класса private.

3 3. Если базовый класс private:

- общедоступные (public) и защищенные (protected) члены базового класса становятся частными (private) элементами производного класса.

- элементы private базового класса остаются для производного класса private.

Во всех рассмотренных случаях отметим, что элементы private базового класса были и остаются недоступными для функций-членов производного класса, пока в производном классе не использован оператор разрешения области видимости (::), пока в описании доступа базового класса не будут явно заданы объявления friend. Например:

class X: A { // класс X является производным от класса А (простое

// наследование), причём по умолчанию спецификатор - private A

}

class Y: B, public C { // класс Y является производным (множественное

// наследование) от B и C. По умолчанию - private B, спецификатор для С- public

}

struct S: D { // struct S - производная от D, по умолчанию для структур

// struct - public D

..,

}

struct T: private D, E { // struct T является производной (множественное

// наследование) от D и E. Спецификатор для D – private D. По умолчанию,

// E - public E

}

Действие спецификаторов доступа к элементам базовых классов при наследовании можно скорректировать при помощи оператора разрешения области видимости (::) в разделах public или protected для производного класса. Например:

class B {// базовый класс int a; //по умолчанию // private public: int b, c; int Bfunc(void); }; class X: private B { // теперь члены базового класса // В::a, b, c и B func() - стали private в классе //наследнике Х.Переменная ав Х недоступна int d; // по умолчанию private public: B::c; // переменная cбыла private; теперь она public: int e; int Xfunc(void); };

int Efunc(X& x); // внешняя по отношению к классам В и Х, требует

// ссылку на объект х класса Х.

Функция Efunc() может использовать только имена членов класса Х с атрибутом public, например х.c, х.e и х.Xfunc.

Функция Xfunc() в X является функцией-членом, поэтому она имеет доступ:

1 к собственным private и public элементам: d, e и Xfunc().

2 к старшим private элементам базового класса В: b и Bfunc().

3 Однако, Xfunc не имеет доступа к private относительно B элементу a.

4 к переменной с класса В явно заданной public c помощью (::) – В::с.

Конструкторы базового класса должны объявляться с атрибутами public или protected. Причём: конструкторы производных классов обязательно вызывают конструкторы базовых классов.

Примеры:

class base1 { int x; public: base1(int i) { x = i; } }; class base2 { int x; public: base2(int i): x(i) {} }; class top: public base1, public base2 { int a, b; public: top(int i, int j): base(i*5), base2(j+i), a(i) { b = j;} };

В случае такой иерархии классов объявление top one(1, 2) приведет к инициализации base 1::х значением 5, а base2::х значением 3. Методы инициализации могут комбинироваться друг с другом (чередоваться).

Базовые классы инициализируются нижнего с верхнего в иерархии в последовательности, указанной в объявлении производного класса при множественном наследовании. Инициализация элементов происходит в последовательности их объявления независимо от последовательности их расположения в списке инициализации.

Class X

{

int a, b;

public:

X(int i, j): a(i), b(a+j) {}

};

Объявление X xobj (2.1) приведет к присваиванию xobj::a числа 2 и xobj::b числа 3.

Конструкторы базовых классов вызываются перед конструированием любых элементов производных классов. Значения данных-членов производного класса не могут изменяться и затем влиять на создание базового класса.

class base { int x; public: base(int i): x(i) {} };   class derived: base { int a; public: derived(int i): a(i*10), base(a) {} //нельзя, т.к. // конструктору base будет передано неинициализированное a }

При внешнем определении конструктора derived он также вызывает конструктор базового класса



Поделиться:


Последнее изменение этой страницы: 2016-04-26; просмотров: 831; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.22.248.208 (0.052 с.)