// ZooAnimal *pz = new Panda;
delete pz;
// Bear *pb = new Panda;
delete pb;
// Panda *pp = new Panda;
delete pp;
// Endangered *pe = new Panda;
delete pe;
Деструктор класса Panda вызывается с помощью механизма виртуализации. После его выполнения по очереди статически вызываются деструкторы Endangered и Bear, а в самом конце – ZooAnimal.
Почленная инициализация и присваивание объекту производного класса, наследующего нескольким базовым, ведут себя точно так же, как и при одиночном наследовании (см. раздел 17.6). Например, для нашего объявления класса Panda
class Panda : public Bear, public Endangered
{ ... };
в результате почленной инициализации объекта ling_ling
Panda yin_yang;
Panda ling_ling = yin_yang;
вызывается копирующий конструктор класса Bear (но, так как Bear производный от ZooAnimal, сначала выполняется копирующий конструктор класса ZooAnimal), затем – класса Endangered и только потом – класса Panda. Почленное присваивание ведет себя аналогично.
Упражнение 18.1
Какие из следующих объявлений ошибочны? Почему?
(a) class CADVehicle : public CAD, Vehicle { ... };
(b) class DoublyLinkedList:
public List, public List { ... };
(c) class iostream:
private istream, private ostream { ... };
Упражнение 18.2
Дана иерархия, в каждом классе которой определен конструктор по умолчанию:
class A { ... };
class B : public A { ... };
class C : public B { ... };
class X { ... };
class Y { ... };
class Z : public X, public Y { ... };
class MI : public C, public Z { ... };
Каков порядок вызова конструкторов в таком определении:
MI mi;
Упражнение 18.3
Дана иерархия, в каждом классе которой определен конструктор по умолчанию:
class X { ... };
class A { ... };
class B : public A { ... };
class C : private B { ... };
class D : public X, public C { ... };
Какие из следующих преобразований недопустимы:
D *pd = new D;
(a) X *px = pd; (c) B *pb = pd;
(b) A *pa = pd; (d) C *pc = pd;
Упражнение 18.4
Дана иерархия классов, обладающая приведенным ниже набором виртуальных функций:
class Base {
public:
virtual ~Base();
virtual ostream& print();
virtual void debug();
virtual void readOn();
virtual void writeOn();
// ...
};
class Derived1 : virtual public Base {
public:
virtual ~Derived1();
virtual void writeOn();
// ...
};
class Derived2 : virtual public Base {
public:
virtual ~Derived2();
virtual void readOn();
// ...
};
class MI : public Derived1, public Derived2 {
public:
virtual ~MI();
virtual ostream& print();
virtual void debug();
// ...
};
Какой экземпляр виртуальной функции вызывается в каждом из следующих случаев:
Base *pb = new MI;
(a) pb-print(); (c) pb-readOn(); (e) pb-log();
(b) pb-debug(); (d) pb-writeOn(); (f) delete pb;
Упражнение 18.5
На примере иерархии классов из упражнения 18.4 определите, какие виртуальные функции активны при вызове через pd1 и pd2:
(a) Derived1 *pd1 new MI;
(b) MI obj;
Derived2 d2 = obj;
18.3. Открытое, закрытое и защищенное наследование
Открытое наследование называется еще наследованием типа. Производный класс в этом случае является подтипом базового; он замещает реализации всех функций-членов, специфичных для типа базового класса, и наследует общие для типа и подтипа функции. Можно сказать, что производный класс служит примером отношения "ЯВЛЯЕТСЯ", т.е. предоставляет специализацию более общего базового класса. Медведь (Bear) является животным из зоопарка (ZooAnimal); аудиокнига (AudioBook) является предметом, выдаваемым читателям (LibraryLendingMaterial). Мы говорим, что Bear – это подтип ZooAnimal, равно как и Panda. Аналогично AudioBook – подтип LibBook (библиотечная книга), а оба они – подтипы LibraryLendingMaterial. В любом месте программы, где ожидается базовый тип, можно вместо него подставить открыто унаследованный от него подтип, и программа будет продолжать работать правильно (при условии, конечно, что подтип реализован корректно). Во всех приведенных выше примерах демонстрировалось именно наследование типа.
Закрытое наследование называют также наследованием реализации. Производный класс напрямую не поддерживает открытый интерфейс базового, но пользуется его реализацией, предоставляя свой собственный открытый интерфейс.