Выбрать главу

Например, при невиртуальном наследовании неквалифицированное обращение к onExhibit() через объект Panda неоднозначно:

// ошибка: неоднозначно при невиртуальном наследовании

Panda yolo( "любитель бамбука" );

yolo.onExhibit();

В данном случае все унаследованные экземпляры имеют равные приоритеты при разрешении имени, поэтому неквалифицированное обращение приводит к ошибке компиляции из-за неоднозначности (см. раздел 18.4.1).

При виртуальном наследовании члену, унаследованному из виртуального базового класса, приписывается меньший приоритет, чем члену с тем же именем, замещенному в производном. Так, унаследованному от Bear экземпляру onExhibit() отдается предпочтение перед экземпляром из ZooAnimal, унаследованному через Raccoon:

// правильно: при виртуальном наследовании неоднозначности нет

// вызывается Bear::onExhibit()

yolo.onExhibit();

Если два или более классов на одном и том же уровне наследования замещают некоторый член виртуального базового, то в производном они будут иметь одинаковый вес. Например, если в Raccoon также определен член onExhibit(), то при обращении к нему из Panda придется квалифицировать имя с помощью оператора разрешения области видимости:

bool Panda::onExhibit()

{

return Bear::onExhibit() &&

Raccoon::onExhibit() &&

! _sleeping;

}

Упражнение 18.13

Дана иерархия классов:

class Class { ... };

class Base : public Class { ... };

class Derived1 : virtual public Base { ... };

class Derived2 : virtual public Base { ... };

class MI : public Derived1,

public Derived2 { ... };

class Final : public MI, public Class { ... };

(a)В каком порядке вызываются конструкторы и деструкторы при определении объекта Final?

(b)Сколько подобъектов класса Base содержит объект Final? А сколько подобъектов Class?

(c)Какие из следующих присваиваний вызывают ошибку компиляции?

Base *pb;

MI *pmi;

Class *pc;

Derived2 *pd2;

(i) pb = new Class; (iii) pmi = pb;

(ii) pc = new Final; (iv) pd2 = pmi;

Упражнение 18.14

Дана иерархия классов:

class Base {

public:

bar( int );

// ...

protected:

int ival;

// ...

};

class Derived1 : virtual public Base {

public:

bar( char );

foo( char );

// ...

protected:

char cval;

// ...

};

class Derived2 : virtual public Base {

public:

foo( int );

// ...

protected:

int ival;

char cval;

// ...

};

class VMI : public Derived1, public Derived2 {};

К каким из унаследованных членов можно обращаться из класса VMI, не квалифицируя имя? А какие требуют квалификации?

Упражнение 18.15

Дан класс Base с тремя конструкторами:

class Base {

public:

Base();

Base( string );

Base( const Base& );

// ...

protected:

string _name;

};

Определите соответствующие конструкторы для каждого из следующих классов:

(a) любой из

class Derived1 : virtual public Vase { ... };

class Derived2 : virtual public Vase { ... };

(b) class VMI : public Derived1, public Derived2 { ... };

(c) class Final : public VMI { ... };

18.6. Пример множественного виртуального наследования A

Мы продемонстрируем определение и использование множественного виртуального наследования, реализовав иерархию шаблонов классов Array (см. раздел 2.4) на основе шаблона Array (см. главу 16), модифицированного так, чтобы он стал конкретным базовым классом. Перед тем как приступать к реализации, поговорим о взаимосвязях между шаблонами классов и наследованием.

Конкретизированный экземпляр такого шаблона может выступать в роли явного базового класса:

class IntStack : private Arrayint {};

Разрешается также произвести его от не шаблонного базового класса:

class Base {};

template class Type

class Derived : public Base {};

Шаблон может выступать одновременно в роли базового и производного классов: