- •2.1 Елементи концепції ооп .. 20
- •1.1 Коментарі.
- •1.2 Прототипи функцій.
- •1.3 Операція розширення області видимості.
- •1.4 Оголошення в операторах.
- •1.5 Перегрузка функцій.
- •1.6 Значення формальних параметрів по замовчуванню.
- •1.7 Посилання та вказівники.
- •1.8 Специфікатор inline
- •1.9 Операції new та delete .
- •1.10 Вказівник на void.
- •1.11 Зв’язування із збереженням типів
- •1.12 Про структури та об’єднання.
- •2.1 Елементи концепції ооп.
- •2.3 Опис протоколу класу.
- •2.4 Передача повідомлень об’єктам.
- •3 Функції-члени.
- •3.1 Функції-члени в межах та за межами формального опису класу.
- •3.2 Про вказівник this.
- •3.3 Перевантаження функцій-членів. Параметри по замовчуванню.
- •4. Конструктори та деструктори.
- •4.1 Поняття про конструктори.
- •4.2 Деструктори.
- •4.3 Досягнення високої ефективності. Конструктор копіювання.
- •5 Глобальні та локальні об’єкти.
- •6 Статична пам’ять та класи.
- •7. Наслідування
- •7.1 Синтаксична реалізація наслідування
- •7.2 Правила доступу до полів даних
- •7.3 Конструктори та деструктори в похідних класах
- •7.4 Використання заміщуючих функцій-членів.
- •7.5 Похідні класи та вказівники.
- •7.6 Ієрархія типів
- •7.7 Множинне наслідування
- •8 Вiртуальнi функцiї та класи
- •8.1 Віртуальні функції.
- •8.2 Чисті віртуальні функції. Абстрактні класи.
- •8.3 Віртуальні деструктори.
- •8.4 Посилання як засіб для реалізації поліморфізму
- •8.5 Технічна реалізація механізму віртуальних функцій.
- •8.6 Віртуальні базові класи
- •8.6.1 Ієрархії класів та наслідування
- •8.6.2 Віртуальні базові класи
- •8.6.3 Виклик конструкторів та віртуальні базові класи.
- •9 Друзі
- •9.1 Дружні класи.
- •9.2 Дружні функції.
- •10 Перевантаження операторiв.
- •10.1 Перевантаження операторів. Загальний підхід.
- •10.2 Перетворення типів.
- •10.3 Перевантаження деяких операторів.
- •10.3.1 Оператор індексування масиву.
- •10.3.2 Перевантаження оператора виклику функції.
- •10.3.3 Оператор доступу до члена класу.
- •10.3.4 Перевантаження операторів інкремента та декремента.
- •10.3.5 Перевантаження операторів управління пам’яттю (new,delete).
- •10.3.6 Перевантаження оператора присвоювання.
- •11.1 Функціональні шаблони
- •11.1.1 Визначення та використання шаблонів функцiй.
- •11.1.2 Перевантаження шаблонiв функцiї.
- •11.1.3 Cпецiалiзованi функцiї шаблона.
- •11.2 Шаблони класів.
- •11.2.1 Визначення шаблонів класу
- •11.2.2 Константи та типи як параметри шаблону
- •11.2.3 Використання шаблонних класів
- •11.2.4 Спецiалiзацiя шаблонiв класу.
- •11.3 Шаблони та конфiгурацiя компiлятора.
- •11.3.1 Шаблони Smart.
- •11.3.2 Шаблони Global I External.
- •12.2 Переадресація вводу-виводу
- •12.3 Розширення потоків для типів кориcтувача
- •12.4 Операції роботи з потоком як дружні
- •12.5 Форматований ввід-вивід
- •12.5.1 Ширина поля
- •12.5.2 Заповнюючий символ
- •12.5.3 Число цифр дійсних чисел
- •12.5.4 Прапорці форматування
- •12.5.5 Маніпулятори
- •12.6 Стан потоку
- •12.7 Файловий ввід-вивід
- •12.7.1 Конструктори файлових потокiв
- •12.7.2 Вiдкриття файлу
- •12.8 Неформатований ввід-вивід
- •12.9 Деякі функції вводу-виводу
- •12.10 Форматування в пам’яті
- •13 Управління виключеннями
- •13.1 Виключення та стек
- •13.2.1 Синтаксис основних конструкцій
- •13.2.1.1 Використання try та сatch
- •13.2.1.2 Використання throw
- •13.2.2 Тип виключення та конструктор копії
- •13.2.3 Пошук відповідного типу виключення
- •13.2.4 Використання terminate() та некеровані виключення
- •13.2.5 Робота з специфікаціями виключень
- •13.2.6 Робота з непередбаченими виключеннями
- •13.2.7 Робота з конструкторами та виключеннями
- •13.2.8 Динамічні об’єкти
- •13.2.9 Передача значень з конструктора та деструктора
- •13.2.10 Робота з ієрархіями виключень
- •13.2.11 Робота з специфічними класами виключень
- •13.3 Структурне управління виключеннями
- •13.3.1 Використання кадрованого управління виключеннями
- •13.3.1.1 Синтаксис
- •13.3.1.2 Про функцію RaiseException()
- •13.3.1.3 Фільтруючий вираз
- •13.3.1.4 Перехоплення виключення процесора
- •13.3.2 Використання завершуючих обробників виключень
8.6 Віртуальні базові класи
8.6.1 Ієрархії класів та наслідування
Похідні класи та їх базові класи утворюють ієрархію, яка може бути надзвичайно складною навіть у відносно простих програмах. А якщо тут згадати ще можливості множинного наслідування, то ситуація взагалі може вийти з під контролю. Не дивно, що у складних ієрархіях класів виникають різні конфлікти. Крім вже розглянутих вище конфліктів імен виникають і конфлікти дещо іншого характеру. Розглянемо такий приклад:
class A
{protected:
int data;
public:
void func( void){//тіло}
};
class B: public A
{//...}
class C: public A
{//...}
class D:public B,public C
{//...}
main()
{D d;
d.f();}
Зразу ж виникає питання з точки зору синтаксису: яка функція f() викликається - унаслідувана класом В чи класом С ? Звичайно, в даному прикладі це семантично зрозуміло. Але чи зрозуміє це компілятор? Адже формально тут виникає розглянутий вище конфлікт імен. Бачимо, що у випадку, коли один клас унаслiдується iншим класом " кiлька разiв " (через множинне наслiдування) можуть виникнути негативні моменти. У прикладi клас A унаслiдується в Д 2 рази, будучи базовим для класiв В i С. Очевидно, що "неприємностi" можуть виникнути і у тому випадку, коли класи В та С мають конструктори, якi iнiцiалiзуть поле data. Тодi клас Д також повинен мати конструктор, хоча б для того, щоб викликати конструктори базових класiв. Але при цьому виникне неоднозначнiсть при iнiцiалiзацiї поля data.
Чи буде виявлена така помилка на етапі компіляції ?
Визначимо ще один клас
class E
: public A,
public D
{\\ . . .}
У цьому випадку навіть на етапі компіляції буде виявлена помилка:
A is inaccessible becouse also in D (А недоступний, оскільки він вже в D).
Для виходу з таких ситуацій використовуються віртуальні базові класи.
8.6.2 Віртуальні базові класи
Синтаксично для оголошення базового класу віртуальним достатньо в базовому списку вказати ключове слово virtual перед атрибутом доступу:
class<ім’я похідного класу>: virtual <атрибут доступу> <ім’я базового класу> , . .
Семантично така конструкція вказує компілятору, що у випадку багатократного унаслідування береться до уваги лише одна “копія” базового класу. При цьому ліквідується небезпека повторного унаслідування. Причому слово virtual повинне бути присутнім у всіх випадках, де можливе повторне унаслідування базового класу. Якщо в нашому прикладі оголосити базовий клас А віртуальним, то ніяких негативних моментів, пов’язаних з повторним унаслідуванням не буде:
class A
{protected:
int data;
public:
void func( void){//тіло}
};
class B: virtual public A
{//...}
class C: virtual public A
{//...}
class D:public B,public C
{//...}
class E
: virtual public A,
public D
{//...}
main()
{D d;
d.f();}
Бачимо, що в описi класiв В i С присутнє слово virtual. При цьому ситуацiя змiниться таким чином, що якщо клас А унаслiдується в кількох класах, то вiн дублюватись не буде . Розглянемо ще один приклад:
class Company {
private:
char*name;
public:
company(const char*s) {name=strdup(s);}
void Display(void) {cout<<name<</ '\n'}
};
class CocaCola:virtual public Company {
public:
CocaCola(void):Company("CocaCola") { }
};
class CocaColaUkr:virtual public Company,
virtual public CocaCola{
public:
CocaColaUkr(void):Company("CocaColaUkr")
{//...}
//...}
// Буде помилка за рахунок використання name.
Щоб її уникнути, потрiбно використовувати вiртуальнi базовi класи
(virtual).
Допишемо до прикладу 1 клас Pepsi:
class Pepsi:public Company{
public:
Pepsi( ):Company("Pepsi"){ }};
class Corporation:public CocaCola,
public Pepsi
{//...};
Тут буде помилка! Pepsi та CocaCola виводяться з company(хоч в CocaCola company є віртуальним). Щоб помилки не було, клас Company повинен бути оголошеним віртуальним в Pepsi.
Опишемо тепер клас Corporation таким чином:
class Corporatin:public CocaCola,
public CocaColaUkr{ }
У цьому випадку помилка відсутня.