Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
63
Добавлен:
16.02.2016
Размер:
455 Кб
Скачать

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

В сложной иерархии классов при множественном наследова­нии может получиться так, что производный класс косвенно унаследует два или более экземпляра одного и того же класса. Рассмотрим пример, который проясняет эту ситуацию.

class IndBase

{

private:

int x;

public:

int GetX(){return x;}

void SetX(int _x){x = _x;}

double var;

};

class Basel: public IndBase

{

//...

};

class Base2: public IndBase

{

//...

};

class Derived: public Basel, public Base2

{

//...

};

main()

{

Derived ob;

ob.var = 5.0;

ob.SetX(0);

int z = ob. GetX () ;

//ob.Basel::var = 5.0;

//ob.Basel::SetX(0) ;

//int z = ob.Basel::GetX ();

return 0;

}

Здесь класс Derived косвенно наследует класс IndBase через свои базовые классы Basel и Base2. Поэтому при ком­пиляции приведенного примера возникнут ошибки, вызванные неоднозначностью обращения к членам класса var и GetX () в строках:

ob.var =5.0;

ob.SetX(0);

int z = ob.GetX() ;

Чтобы избежать этой неоднозначности, можно использовать квалификацию имен, применив операцию разрешения видимости:

ob.Basel::var = 5.0;

ob.Basel::SetX(0);

int z = ob.Basel::GetX();

Можно также квалифицировать эти вызовы следующим образом:

ob.Base2::var = 5.0;

ob.Base2::SetX(0);

int z = ob.Base2::GetX();

Хотя этот способ и позволяет избежать неоднозначности при вызове, тем не менее класс IndBase будет включен в состав класса Derived дважды, увеличивая его размер. Избежать по­вторного включения косвенного базового класса в производный класс можно, дав указание компилятору использовать виртуаль­ный базовый класс. Это осуществляется с помощью ключевого слова virtual, которое указывается перед спецификатором на­следуемого доступа или после него. Следующий пример является модифицированным вариантом предыдущего, использующим класс IndBase в качестве виртуального базового класса.

class IndBase

{

private:

int x;

public:

int GetX(){return x;}

void SetX(int _x){x = _x;} double var;

};

class Basel: virtual public IndBase

{

//...

};

class Base2: virtual public IndBase

{

//...

};

class Derived: public Basel, public Base2

{

//...

};

main()

{

Derived ob;

ob.var = 5.0;

ob.SetX(0);

int z = ob.GetX() ;

return 0;

}

В этом случае класс Derived содержит один экземпляр клас­са IndBase. и вызовы

ob.var = 5.0;

ob.SetX(0);

int z = ob.GetX() ;

не приводят к появлению сообщений компилятора об ошибках, связанных с неоднозначностью.

Отсюда следует общая рекомендация: если разрабатывается иерархия классов и данный класс наследуется несколькими клас­сами, включите перед спецификаторами наследуемого доступа ключевое слово virtual, то есть наследуйте его как виртуаль­ный базовый класс.

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

class Basel{};

class Base2{};

class Derived: public Basel, public Base2 {};

main()

{

Derived ob;

//...

return 0;

}

конструкторы вызываются в следующем порядке:

Basel () ;

Base2() ;

Derived () ;

Объявление базового класса виртуальным изменяет порядок вызова конструкторов при создании экземпляра производного класса. Конструкторы виртуальных базовых классов вызываются первыми, раньше конструкторов невиртуальных базовых классов.

Если виртуальных базовых классов несколько, их конструкторы вызываются в порядке их объявления в списке наследования. За­тем вызываются конструкторы невиртуальных базовых классов в порядке их объявления в списке наследования и, наконец, вызы­вается конструктор производного класса. Если какой-то вирту­альный класс является производным невиртуального базового класса, этот невиртуальный базовый класс конструируется пер­вым (иначе нельзя будет вызвать конструктор виртуального базо­вого класса). Например, в случае объявления

class Basel{};

class Base2{};

class Derived: public Basel, virtual public Base2 { } ;

main()

{

Derived ob;

//…;

return 0;

}

конструкторы вызываются в следующем порядке:

Base2() ;

Basel () ;

Derived();

Если иерархия классов содержит несколько экземпляров вир­туального базового класса, этот базовый класс конструируется только один раз. Если все же существуют как виртуальный, так и невиртуальный экземпляры этого базового класса, конструктор базового класса вызывается один раз для всех виртуальных эк­земпляров этого базового класса, а затем еще раз для каждого невиртуального экземпляра этого базового класса.

Деструкторы вызываются в порядке, в точности обратном конструкторам.

Соседние файлы в папке ЯзыкС++Глушаков