Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Курс лекцій.doc
Скачиваний:
15
Добавлен:
03.11.2018
Размер:
1.12 Mб
Скачать

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{ }

У цьому випадку помилка відсутня.