Как отмечено в комментариях этой программы, оба класса derived1 и derived2 наследуют класс base. Но класс derived3 наследует как класс derived1, так и класс  derived2. В результате в объекте типа derived3 присутствуют две копии класса base, поэтому, например, в таком выражении

ob.i = 20;

не ясно, на какую именно копию члена i здесь дана ссылка: на член, унаследованный от класса derived1 или от класса derived2? Поскольку в объекте ob присутствуют обе копии класса base, то в нем существуют и два члена ob.is! Потому-то эта инструкция и является наследственно неоднозначной (существенно неопределенной).

Есть два способа исправить предыдущую программу. Первый состоит в применении оператора разрешения контекста (разрешения области видимости), с помощью которого можно "вручную" указать нужный член i. Например, следующая версия этой программы успешно скомпилируется и выполнится ожидаемым образом.

/* Эта программа использует оператор разрешения контекста для выбора нужного члена i.

*/

#include

using namespace std;

class base {

 public:

  int i;

};

// Класс derived1 наследует класс base.

class derived1 : public base { public: int j;};

// Класс derived2 наследует класс base.

class derived2 : public base { public: int k;};

/* Класс derived3 наследует оба класса derived1 и derived2. Это означает, что в классе derived3 существует две копии класса base!

*/

class derived3 : public derived1, public derived2 {

 public:

  int sum;

};

int main()

{

 derived3 ob;

 ob.derived1::i = 10; // Контекст разрешен, используется член i класса derived1.

 ob.j = 20;

 ob.k = 30;

 // Контекст разрешен и здесь.

 ob.sum = ob.derived1::i + ob.j + ob.k;

 // Неоднозначность ликвидирована и здесь.

 cout << ob.derived1::i << " ";

 cout << ob.j << " " << ob.k << " ";

 cout << ob.sum;

 return 0;

}

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

Применение оператора "::" позволяет программе "ручным способом" выбрать версию класса base (унаследованную классом derived1). Но после такого решения возникает интересный вопрос: а что, если в действительности нужна только одна копия класса base? Можно ли каким-то образом предотвратить включение двух копий в класс derived3? Ответ, как, наверное, вы догадались, положителен. Это решение достигается с помощью виртуальных базовых классов.

Если два (или больше) класса выведены из общего базового класса, мы можем предотвратить включение нескольких его копий в объекте, выведенном из этих классов, что реализуется путем объявления базового класса при его наследовании виртуальным. Для этого достаточно предварить имя наследуемого базового класса ключевым словом virtual.

Для иллюстрации этого процесса приведем еще одну версию предыдущей программы. На этот раз класс derived3 содержит только одну копию класса base.

// Эта программа использует виртуальные базовые классы.

#include

using namespace std;

class base {

 public:

 int i;

};

// Класс derived1 наследует класс base как виртуальный.

class derived1 : virtual public base { public: int j;};

// Класс derived2 наследует класс base как виртуальный.

class derived2 : virtual public base { public: int k;};

/* Класс derived3 наследует оба класса derived1 и derived2. На этот раз он содержит только одну копию класса base.

*/

class derived3 : public derived1, public derived2 {

 public:

  int sum;

};

int main()

{

 derived3 ob;

Перейти на страницу:

Все книги серии Изучайте C++ с профессионалами

Похожие книги