Контрольные вопросы для подготовки и самостоятельной работы
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 он также вызывает конструктор базового класса