Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ЛР2_ООП.doc
Скачиваний:
2
Добавлен:
14.11.2019
Размер:
191.49 Кб
Скачать

Віртуальні базові класи

У складній ієрархії класів при множинному успадкуванні може вийти так, що похідний клас побічно успадкує два або більше екземплярів одного і того самого класу. Розглянемо приклад, який пояснює цю ситуацію.

class IndBase

{

private:

int x;

public:

int GetX(){return x;}

void SetX(int_x){x=_x;}

double var;

};

class Base1: public IndBase

{

};

class Base2: public IndBase

{

};

Class Derived: public Base1, public Base2

{

};

main()

{

Derived ob;

ob.var=5.0;

ob.SetX(0);

int z=ob.GetX();

//ob.Base1::var=5.0;

//ob.Base1::SetX(0);

//int z=ob.Base1::GetX();

return 0;

}

Тут клас Derived побічно наслідує клас IndBase через свої базові класи Base1 і Base2. Тому при компіляції наведеного прикладу виникнуть помилки, які викликані неоднозначністю звернення до членів класу var і GetX() в рядках:

ob.var=5.0;

ob.SetX(0);

int z=ob.GetX();

Щоб уникнути цю неоднозначність, можна використати кваліфікацію імен, використавши операцію розширення видимості:

ob.Base1::var=5.0;

ob.Base1::SetX(0);

int z=ob.Base1::GetX();

Можна також кваліфікувати ці виклики наступним чином:

ob.Base2::var=5.0;

ob.Base2::SetX(0);

int z=ob.Base2::GetX();

Хоча цей спосіб і дозволяє уникнути неоднозначності при виклику, тим не менше клас IndBase буде входити в склад класу Derived двічі, збільшуючи його розмір. Уникнути повторного включення побічного базового класу в похідний клас можна давши вказівку компілятору використати віртуальний базовий клас. Це здійснюється за допомогою ключового слова virtual, яке вказується перед специфікатором доступу, який успадковується або після нього. Наступний приклад є модифікованим варіантом попереднього, який використовує клас IndBase в якості віртуального базового класу.

class IndBase

{

private:

int x;

public:

int GetX(){return x;}

void SetX(int_x){x=_x;}

double var;

};

class Base1: virtual public IndBase

{

};

class Base2: virtual public IndBase

{

};

Class Derived: public Base1, public Base2

{

};

main()

{

Derived ob;

ob.var=5.0;

ob.SetX(0);

int z=ob.GetX();

return 0;

}

В цьому випадку клас Derived містить один екземпляр класу IndBase, і виклики

ob.var=5.0;

ob.SetX(0);

int z=ob.GetX();

не приводять до появи повідомлень компілятора про помилки, які пов’язані з неоднозначністю.

Звідси випливає загальна рекомендація: якщо розробляється ієрархія класів і даний клас успадковується декількома класами, включіть перед специфікаторами доступу, який успадковується ключове слово virtual, тобто успадкуйте його як віртуальний базовий клас.

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

class Base1{};

class Base2{};

class Derived: public Base1,

public Base2{};

main()

{

Derived ob;

return 0;

}

конструктори викликаються так:

Base1();

Base2();

Derived();

Оголошення базового класу віртуальним змінює порядок виклику конструкторів при створенні екземпляру похідного класу. Конструктори віртуальних базових класів викликаються перед конструкторами невіртуальних базових класів. Якщо віртуальних базових класів декілька, то їх конструктори викликаються в порядку їх оголошення у списку успадкування. Далі викликаються конструктори невіртуальних базових класів в порядку їх оголошення у списку успадкування і, нарешті, викликається конструктор похідного класу. Якщо якийсь віртуальний клас є похідним невіртуального базового класу, цей невіртуальний базовий клас будується першим (інакше не можна буде викликати конструктор віртуального базового класу). Наприклад, у випадку оголошення

class Base1{};

class Base2{};

class Derived: public Base1,

virtual public Base2{};

main()

{

Derived ob;

return 0;

}

конструктори викликаються так:

Base2();

Base1();

Derived();

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

Деструктори викликаються в порядку, оберненому конструкторам.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]