С++ для начинающих

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


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

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

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

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

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

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

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

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.

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


    (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 { ... };


    Содержание раздела