ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ В C++ (4-Е ИЗДАНИЕ) (часть 11) онлайн

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

Прежде чем расстаться с темой виртуальных элементов программирования, нам

следует коснуться вопроса виртуальных базовых классов, так как они имеют от-

ношение к множественному наследованию.

Рассмотрим ситуацию, представленную на рис. 11.3. Базовым классом явля-

ется parent, есть два порожденных класса — Child1, child2 и есть еще четвертый

класс — Grandchild, порожденный одновременно классами Child1 и Child2.

В такой ситуации проблемы могут возникнуть, если метод класса Grandchild

захочет получить доступ к данным или функциям класса Parent. Что в этом слу-

чае будет происходить, показано в программе NORMBASE.

 

Рис. 11.3. Виртуальные базовые классы

Листинг 11.7. Программа NORMBASE

 

// normbase.cpp

// неоднозначная ссылка на базовый класс

class Parent

    {

    protected:

Листинг 11.7 (продолжение)

 

int basedata;

    };

class Child1 : public Parent

    { };

class Child2 : public Parent

    { };

class Grandchild : public Child1, public Child2

    {

    public:

      int getdata()

      { return basedata; }   // ОШИБКА: неоднозначность

    };

Ошибка компилятора возникла вследствие того, что метод Getdata() из класса

Grandchild попытался получить доступ к basedata из класса parent. И что же в этом

такого? Все дело в том, что каждый из порожденных классов (Child1 и Child2) на-

следует свою копию базового класса Parent. Эта копия называется подобъектом.

Каждый из двух подобъектов содержит собственную копию данных базового

класса, включая basedata. Затем, когда Grandchild ссылается на basedata, к какой

из двух копий базового класса он получает доступ? Ситуация неоднозначная,

о чем компилятор и сообщает.            

Для устранения неоднозначности сделаем Child1 и Child2 наследниками вир-

туального базового класса, как показано в примере VIRTBASE.

Листинг 11.8. Программа VIRTBASE

// virtbase.cpp

// Виртуальные базовые классы

class Parent

    {

    protected:

      int basedata;

    };

class Child1 : virtual public Parent // наследует копию

                                     // класса Parent

    { };

class Child2 : virtual public Parent // наследует копию

                                     // класса Parent

    { };

class Grandchild : public Child1, public Child2

    {

    public:

      int getdata()

        { return basedata; }         // OK: только одна копия

                                     //класса Parent

    };

 

Использование ключевого слова virtual в этих двух классах приводит к тому,

что они наследуют единый общий подобъект базового класса Parent. Так как

теперь у нас есть только одна копия basedata, неоднозначность при обращении

к базовому классу устраняется.

Необходимость использования виртуального базового класса показывает, что

при множественном наследовании возникает ряд концептуальных проблем,

поэтому такой подход должен использоваться осторожно.

 

 

11