Лекція № 15
Тема: Наслідування. Доступ до членів класу.
План
1. Управління доступом до членів базового класу
2. Наслідування і захищені члени
3. Захищене наслідування
4. Множинне наслідування
Наслідування — один з наріжних каменів об'єктно-орієнтованого програмування, оскільки воно дозволяє створювати ієрархічні класифікації Використовуючи Наслідування, можна створювати загальні класи, що визначають властивості, характерні для всієї сукупності споріднених класів. Ці класи можуть успадковувати властивості один у одного, додаючи до них свої власні унікальні характеристики.
Згідно стандартної термінології мови C++ клас, лежачий в основі ієрархії, називається базовим (base class), а клас, що успадковує властивості базового класу, — похідним (derived class). Похідні класи, у свою чергу, можуть бути базовими по відношенню до інших класів.
У мові C++ передбачений могутній і гнучкий механізм наслідування.
Управління доступом до членів базового класу
При наслідуванні члени базового класу стають членами похідного класу. Як правило, для наслідування використовується наступна синтаксична конструкція.
с1ass імя-похідного-класу: рівень_доступу імя-базового-класу
{
// тіло класу
}
Параметр рівень_доступу визначає статус членів базового класу в похідному класі. Як цей параметр використовуються специфікатори public, private або protected. Якщо рівень доступу не вказаний, то для похідного класу за умовчанням використовується специфікатор private, а для похідної структури - public. Розглянемо варіанти, що виникають в цих ситуаціях. (Специфікатор protected буде описаний в наступному розділі.)
Якщо рівень доступу до членів базового класу задається специфікатором publiс то всі відкриті і захищені члени базового класу стають відкритими і захищеними членами похідного класу. При цьому закриті члени базового класу не міняють свого статусу і залишаються недоступними членам похідного. Як демонструє наступна програма, об'єкти класу derived можуть безпосередньо посилатися на відкриті члени класу base.
#include <iostream>
using namespace std;
class base {
int i, j;
public:
void set(int а, int b) { i=a; j=b; }
void show() { cout « i << " " « j « "\n"; }
};
class derived : public base {
int k;
public:
derived(int x) { k=x; }
void showkO { cout « k « "\n"; }
};
int main() {
derived ob (3) ;
fcb.set(l, 2); // Звернення до члена класу base
ob.showO; // Звернення до члена класу base
job.showkO; // Звернення до члена класу derived
return 0 ;
}
Якщо властивості базового класу успадковуються за допомогою специфікатора доступу private, всі відкриті і захищені члени базового класу стають закритими членами похідного класу. Наприклад, наступна програма навіть не буде скомпільована, оскільки обидві функції set() і show() тепер є закритими членами класу derived.
Ця програма не буде скомпільована.
#include <iostream>
using namespace std;
class base {
int i, j ;
public:
void set (int а, int b) { i=a; j=b; }
void show() { cout « i « " " « j « "\n";}
};

Відкриті члени класу base є закритими членами класу derived.
class derived : private base {
int k;
public:
derived(int x) { k=x; }
};
void main() {
derived ob (3) ;
ob.set (1, pb.show());
// Помилка, доступ до функції set() заборонений.
ob.show();
// Помилка, доступ до функції show() заборонений.
}
При закритому наслідуванні всі відкриті і захищені члени базового класу стають закритими членами похідного класу. Це означає, що вони залишаються доступними членам похідного класу, але недоступні решті елементів програми, що не є членами базового або похідного класів.

Наслідування і захищені члени
Специфікатор protected підвищує гнучкість механізму наслідування. Якщо член класу оголошений захищеним (protected), то поза класом він недоступний. З цієї точки зору захищений член класу нічим не відрізняється від закритого. Єдине виключення з цього правила стосується наслідування. У цій ситуації захищений член класу істотно відрізняється від закритого.
Як вказувалося в попередньому розділі, закритий член базового класу не доступний іншим елементам програми, включаючи похідний клас. Проте захищені члени базового класу поводяться інакше. При відкритому наслідуванні захищені члени базового класу стають захищеними членами похідного класу до отже, доступні решті членів похідного класу. Іншими словами захищені члени класу по відношенню до свого класу є закритими і в той же час, можуть успадковуватися похідним класом. Розглянемо приклад.
#include <iostream>
using namespace std;
class base {
protected:
int i, j; // Закриті по відношенню до класу base
// але доступні класу derived.
public:
void set(int а, int b) { i=a; j=b; }
void showO { cout « i « " " « j « "\n"; }
};
class derived : public base {
int k;
public:
// Клас derived має доступ до членів i і j з класу base
void setk() { k=i*j; }
void showkO { cout « k << "\n"; }
};
int main() {
derived ob;
ob.set(2, 3);
// Все гаразд, цей член доступний класу derived
ob.showO; // Все гаразд, цей член доступний класу derived
ob.setk();
ob.showk();
return 0;
}

У даному прикладі, оскільки клас derived успадковує властивості класу base за допомогою відкритого наслідування, а змінні i і j оголошені захищеними, функція seek () з класу derived має до них доступ. Якби змінні i і j були оголошені в класі base закритими, то клас derived не мав би до них доступу, і програму не можна було скомпілювати.
Якщо похідний клас є базовим по відношенню до іншого похідного класу, то будь-який захищений член початкового базового класу, відкрито успадкований першим похідним класом, також може успадковуватися другим похідним класом як захищений член.
Захищене наслідування
До базового класу можна застосовувати механізм захищеного наслідування. При цьому всі відкриті і захищені члени базового класу стають захищеними членами похідного класу. Розглянемо приклад.
#include <iostream>
using namespace std;
class base {
protected:
int i, j; // Закриті члени класу base
// доступні класу derived.
public:
void setij(int а, int b) { i=a; j=b; }
void showij() { cout « i « " " « j « "\n"; }
};
class derived : protected base {
int k;
public:
//Клас derived має доступ до членів i, j і setij()
// з класу base
void setk() { setijd(10, 12); до = i*j; }
// Звідси можна викликати функцію showij().
void showall() { cout << k « " "; showij(); }
};
int main() {
derived ob;
ob.setij (2, 3);
// Невірно, функція setij() є закритим членом класу derived.
ob.setk();
// Вірно, викликається відкритий член класу derived.
ob.showall();
// Вірно, викликається відкритий член класу derived.
//ob.showij () ; // Невірно, функція showij() є
// захищеним членом класу derived
return 0;
}
Як випливає з коментарів, не дивлячись на те що функції setij() і showij() є відкритими членами класу base, в класі derived, утвореному за допомогою захищеного наслідування, вони стають захищеними. Це означає, що функції main() вони не доступні.
Множинне наслідування
Похідний клас може одночасно успадковувати властивості декілька базових Наприклад, в програмі, приведеній нижче, клас derived успадковує властивості класів base1 і base2.
Приклад множинного наслідування.
include <iostream>
using namespace std;
class base1 {
protected:
int x;
puclic:
void showx() { cout << x « "\n"; }
};
class base2 {
protected:
int у;
public:
void showy() {cout « у « "\n";}
};

// Множинне Наслідування.
class derived: public basel, public base2 {
public:
void set(int i, int j) { x=i; y=j; }
};
int main() {
derived ob;
ob.set(10, 20); // Ця функція належить класу derived.
ob.showx(); // Ця функція належить класу basel.
ob.showy(); // Ця функція належить класу base2.
return 0;
}
Як видно, при множинному наслідуванні імена базових класів перераховуються в списку і розділяються комами, причому перед кожним ім'ям базового класу указується свій специфікатор доступу.