Урок №165. Спадкування і специфікатор доступу protected

  Юрій  | 

  Оновл. 24 Лют 2021  | 

 63

На попередніх уроках ми говорили про те, як працює спадкування в мові C++. У всіх наших прикладах ми використовували відкрите спадкування.

На цьому уроці ми розглянемо детально цей тип спадкування, а також два інших типи: private і protected. Також поговоримо про те, як ці типи наслідувань взаємодіють зі специфікаторами доступу для дозволу або обмеження доступу до членів.

Специфікатор доступу protected

Ми вже розглядали специфікатори доступу private і public, які визначають, хто може мати доступ до членів класу. В якості нагадування: доступ до public-членів відкритий для всіх, до private-членів доступ мають тільки члени того ж класу, в якому знаходиться private-член. Це означає, що дочірні класи не можуть напряму звертатися до private-членів батьківського класу!

Все просто.

Примітка: public = «відкритий», private = «закритий», protected = «захищений».

У мові C++ є третій специфікатор доступу, про який ми ще не говорили, тому що він корисний тільки в контексті спадкування. Специфікатор доступу protected відкриває доступ до членів класу дружнім і дочірнім класам. Доступ до protected-члену поза тілом класу закритий.

У прикладі, наведеному вище, ви можете бачити, що член m_protected класу Parent напряму доступний дочірньому класу Child, але доступ до нього для членів ззовні — закритий.

Коли слід використовувати специфікатор доступу protected?

До protected-членів батьківського класу доступ відкритий для членів дочірнього класу, а це означає, що якщо ви пізніше зміните що-небудь в protected-члені (тип даних, значення), то вам доведеться внести зміни як до батьківського, так і в усі дочірні класи. Тому використання специфікатора доступу protected найбільш корисне, коли ви будете наслідувати тільки свої ж класи і кількість дочірніх класів буде невелика. Таким чином, якщо ви внесете зміни в реалізацію батьківського класу, і вам знадобиться оновити всі дочірні класи, то ви зможете зробити ці оновлення самі і це не займе багато часу (тому що дочірніх класів буде небагато).

Створення private-членів надає кращу інкапсуляцію і ізолює батьківські класи від змін, викликаних дочірніми класами. Але ціна цьому додаткове створення відкритого або захищеного інтерфейсу (способу взаємодії інших об’єктів з класами і їх членами, тобто геттери і сеттери). Це додаткова робота, яка не варто того, якщо ви самі працюєте зі своїми ж класами (чужі класи не звертаються до вашого класу) і кількість дочірніх класів невелика.

Типи спадкувань. Доступ до членів

Існує три типи спадкування класів:

   public;

   private;

   protected.

Для визначення типу спадкування потрібно просто вказати потрібне ключове слово біля успадкованого класу:

Якщо ви самі не визначили тип спадкування, то в мові C++ за замовчуванням буде обраний тип спадкування private (аналогічно і для членів класу, які за замовчуванням є private, якщо не вказано інше).

Це дає нам 9 комбінацій: 3 специфікатори доступу (public, private і protected) і 3 типи спадкування (public, private і protected).

Так в чому ж різниця між ними? Якщо коротко, то при спадкуванні специфікатор доступу члена батьківського класу може бути змінений в дочірньому класі (в залежності від типу спадкування). Іншими словами, члени, які були public або protected в батьківському класі, можуть стати private в дочірньому класі.

Це може здатися трохи заплутаним, але все не так уже й погано. Ми зараз з усім цим розберемося, але перед цим згадаємо наступні правила:

   Клас завжди має доступ до своїх (не успадкованих) членів.

   Доступ до члену класу базується на його специфікаторі доступу.

   Дочірній клас має доступ до успадкованих членів батьківського класу на основі специфікатора доступу цих членів в батьківському класі.

Спадкування типу public

Відкрите спадкування є одним з найбільш використовуваних типів спадкування. Дуже рідко ви побачите або будете використовувати інші типи. На щастя, відкрите спадкування є найлегшим і найпростішим з усіх типів. Коли ви відкрито наслідуєте батьківський клас, то успадковані public-члени залишаються public, успадковані protected-члени залишаються protected, а успадковані private-члени залишаються недоступними для дочірнього класу. Нічого не змінюється.

Специфікатор доступу в батьківському класі Специфікатор доступу при спадкуванні типу public в дочірньому класі
public public
private Недоступний
protected protected

Наприклад:

Правило: Використовуйте відкрите спадкування, якщо у вас немає вагомих причин робити інакше.

Спадкування типу private

При закритому спадкуванні всі члени батьківського класу наслідуються як закриті. Це означає, що private-члени залишаються недоступними, а protected- і public-члени стають private в дочірньому класі.

Зверніть увагу, це не впливає на те, як дочірній клас отримує доступ до членів батьківського класу! Це впливає тільки на те, як іншими об’єктами здійснюється доступ до цих членів через дочірній клас:

Резюмуємо:

Специфікатор доступу в батьківському класі Специфікатор доступу при спадкуванні типу private в дочірньому класі
public private
private Недоступний
protected private

Закрите спадкування може бути корисним, коли дочірній клас не має очевидного зв’язку з батьківським класом, але використовує його в своїй реалізації. В такому випадку ми не хочемо, щоб відкритий інтерфейс батьківського класу був доступний через об’єкти дочірнього класу (як це було, коли ми використовували відкритий тип спадкування).

На практиці спадкування типу private використовується рідко.

Спадкування типу protected

Цей тип спадкування майже ніколи не використовується, за винятком особливих випадків. З захищеним спадкуванням, public- і protected-члени стають protected, а private-члени залишаються недоступними.

Оскільки цей тип спадкування дуже рідко використовується, то ми пропустимо приклад на практиці і відразу перейдемо до таблиці:

Специфікатор доступу в батьківському класі Специфікатор доступу при спадкуванні типу protected в дочірньому класі
public protected
private Недоступний
protected protected

Фінальний приклад

Клас Parent може звертатися до своїх членів безперешкодно. Доступ до m_public відкритий для всіх. Дочірні класи можуть звертатися як до m_public, так і до m_protected:

Клас D2 може безперешкодно звертатися до своїх членів. D2 має доступ до членів m_public і m_protected класу Parent, але не до m_private. Оскільки D2 наслідує клас Parent закрито, то m_public і m_protected тепер стають закритими при доступі через D2. Це означає, що інші об’єкти не зможуть отримати доступ до цих членів через використання об’єкту D2, а також будь-які інші класи, які будуть дочірніми класу D2, не матимуть доступ до цих членів:

Клас D3 може безперешкодно звертатися до своїх членів. D3 має доступ до членів m_public2 і m_protected2 класу D2, але не до m_private2. Оскільки D3 наслідує D2 відкрито, то m_public2 і m_protected2 зберігають свої специфікатори доступу і залишаються public і protected при доступі через D3. D3 не має доступ до m_private класу Parent. Він також не має доступ до m_protected або m_public класу Parent, обидва з яких стали закритими, коли D2 успадкував їх.

Висновки

Спосіб взаємодії специфікаторів доступу, типів спадкування і дочірніх класів може викликати плутанину. Щоб це усунути, з’ясуємо все ще раз:

   По-перше, клас завжди має доступ до своїх власних НЕ успадкованих членів (і дружні йому класи також мають доступ). Специфікатори доступу впливають тільки на те, чи можуть об’єкти поза класом і дочірні класи звертатися до цих членів.

   По-друге, коли дочірні класи успадковують члени батьківських класів, то члени батьківського класу можуть змінювати свої специфікатори доступу в дочірньому класі. Це ніяк не впливає на власні (не успадковані) члени дочірніх класів (які визначені в дочірньому класі і мають свої власні специфікатори доступу). Це впливає тільки на те, чи можуть об’єкти ззовні і класи, дочірні нашим дочірнім класам, отримати доступ до успадкованих членів батьківського класу.

Загальна таблиця специфікаторів доступу і типів спадкування:

Специфікатор доступу в батьківському класі Специфікатор доступу при спадкуванні типу public в дочірньому класі Специфікатор доступу при спадкуванні типу private в дочірньому класі Специфікатор доступу при спадкуванні типу protected в дочірньому класі
public public private protected
private Недоступний Недоступний Недоступний
protected protected private protected

Хоча у вищенаведених прикладах ми розглядали використання змінних-членів, ці правила дійсні для всіх членів класів (і для методів, і для типів, оголошених всередині класу).

Оцінити статтю:

1 Зірка2 Зірки3 Зірки4 Зірки5 Зірок (2 оцінок, середня: 5,00 з 5)
Loading...

Залишити відповідь

Ваш E-mail не буде опублікований. Обов'язкові поля відмічені *