18.5.1. Объявление виртуального базового класса
18.5.1. Объявление виртуального базового класса
Для указания виртуального наследования в объявление базового класса вставляется модификатор virtual. Так, в данном примере ZooAnimal становится виртуальным базовым для Bear и Raccoon:
// взаимное расположение ключевых слов public и virtual
// несущественно
class Bear : public virtual ZooAnimal { ... };
class Raccoon : virtual public ZooAnimal { ... };
Виртуальное наследование не является явной характеристикой самого базового класса, а лишь описывает его отношение к производному. Как мы уже отмечали, виртуальное наследование – это разновидность композиции по ссылке. Иначе говоря, доступ к подобъекту и его нестатическим членам косвенный, что обеспечивает гибкость, необходимую для объединения нескольких виртуально унаследованных подобъектов базовых классов в один разделяемый экземпляр внутри производного. В то же время объектом производного класса можно манипулировать через указатель или ссылку на тип базового, хотя последний является виртуальным. Например, все показанные ниже преобразования базовых классов Panda выполняются корректно, хотя Panda использует виртуальное наследование:
extern void dance( const Bear* );
extern void rummage( const Raccoon* );
extern ostream&
operator( ostream&, const ZooAnimal& );
int main()
{
Panda yin_yang;
dance( &yin_yang ); // правильно
rummage( &yin_yang ); // правильно
cout yin_yang; // правильно
// ...
}
Любой класс, который можно задать в качестве базового, разрешается сделать виртуальным, причем он способен содержать все те же элементы, что обычные базовые классы. Так выглядит объявление ZooAnimal:
#include iostream
#include string
class ZooAnimal;
extern ostream&
operator( ostream&, const ZooAnimal& );
class ZooAnimal {
public:
ZooAnimal( string name,
bool onExhibit, string fam_name )
: _name( name ),
_onExhibit( onExhibit ), _fam_name( fam_name )
{}
virtual ~ZooAnimal();
virtual ostream& print( ostream& ) const;
string name() const { return _name; }
string family_name() const { return _fam_name; }
// ...
protected:
bool _onExhibit;
string _name;
string _fam_name;
// ...
};
К объявлению и реализации непосредственного базового класса при использовании виртуального наследования добавляется ключевое слово virtual. Вот, например, объявление нашего класса Bear:
class Bear : public virtual ZooAnimal {
public:
enum DanceType {
two_left_feet, macarena, fandango, waltz };
Bear( string name, bool onExhibit=true )
: ZooAnimal( name, onExhibit, "Bear" ),
_dance( two_left_feet )
{}
virtual ostream& print( ostream& ) const;
void dance( DanceType );
// ...
protected:
DanceType _dance;
// ...
};
А вот объявление класса Raccoon:
class Raccoon : public virtual ZooAnimal {
public:
Raccoon( string name, bool onExhibit=true )
: ZooAnimal( name, onExhibit, "Raccoon" ),
_pettable( false )
{}
virtual ostream& print( ostream& ) const;
bool pettable() const { return _pettable; }
void pettable( bool petval ) { _pettable = petval; }
// ...
protected:
bool _pettable;
// ...
};