Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C++ для начинающих.pdf
Скачиваний:
183
Добавлен:
01.05.2014
Размер:
3.97 Mб
Скачать

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

Изменим наш класс Bear так, чтобы он имел собственную реализацию функции-члена onExhibit(), предоставляемой также ZooAnimal:

bool Bear::onExhibit() { ... }

Теперь обращение к onExhibit() через объект Bear разрешается в пользу экземпляра,

Bear winnie( "любитель меда" );

определенного в этом классе:

winnie.onExhibit(); // Bear::onExhibit()

Обращение же к onExhibit() через объект Raccoon разрешается в пользу функции-

Raccoon meeko( "любитель всякой еды" );

члена, унаследованной из ZooAnimal:

meeko.onExhibit();

// ZooAnimal::onExhibit()

Производный класс Panda наследует члены своих базовых классов. Их можно отнести к одной из трех категорий:

члены виртуального базового класса ZooAnimal, такие, как name() и family(), не замещенные ни в Bear, ни в Raccoon;

член onExhibit() виртуального базового класса ZooAnimal, наследуемый при обращении через Raccoon и замещенный в классе Bear;

специализированные в классах Bear и Raccoon экземпляры функции print() из

ZooAnimal.

Можно ли, не опасаясь неоднозначности, напрямую обращаться к унаследованным членам из области видимости класса Panda? В случае невиртуального наследования – нет: все неквалифицированные ссылки на имя неоднозначны. Что касается виртуального наследования, то прямое обращение допустимо к любым членам из первой и второй категорий. Например, дан объект класса Panda:

Panda spot( "Spottie" );

Тогда инструкция

spot.name();

вызывает разделяемую функцию-член name() виртуального базового ZooAnimal, а инструкция

spot.onExhibit();

вызывает функцию-член onExhibit() производного класса Bear.

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

Например, при невиртуальном наследовании неквалифицированное обращение к

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

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

onExhibit() через объект Panda неоднозначно: yolo.onExhibit();

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

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

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

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

предпочтение перед экземпляром из ZooAnimal, унаследованному через Raccoon: 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?

Base

*pb;

MI

*pmi;

Class

*pc;

Derived2 *pd2;

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

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

(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

class Base { public:

Base();

Base( string ); Base( const

Base& );

// ...

protected: string _name;

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

};

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