Например, если класс Panda использует синтезируемые функции-члены, то инициализация объекта ling_ling вызовет конструктор копий класса Bear, который в свою очередь вызовет конструктор копий класса ZooAnimal прежде, чем выполнить конструктор копий класса Bear:

Panda ying_yang("ying_yang");

Panda ling_ling = ying_yang; // использует конструктор копий

Как только часть Bear объекта ling_ling создана, выполняется конструктор копий класса Endangered, создающий соответствующую часть объекта. И наконец, выполняется конструктор копий класса Panda. Аналогично для синтезируемого конструктора перемещения.

Синтезируемый оператор присвоения копии ведет себя так же, как и конструктор копий. Сначала он присваивает часть Bear (и его часть ZooAnimal) объекта, затем часть Endangered и наконец часть Panda. Оператор присвоения при перемещении ведет себя подобным образом.

Упражнения раздела 18.3.1

Упражнение 18.21. Объясните следующие объявления. Найдите все ошибки и объясните их причину:

(a) class CADVehicle : public CAD, Vehicle { ... };

(b) class DblList: public List, public List { ... };

(c) class iostream: public istream, public ostream { ... };

Упражнение 18.22. С учетом следующей иерархии класса, в которой у каждого класса определен стандартный конструктор:

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;

<p>18.3.2. Преобразования и несколько базовых классов</p>

При одиночном наследовании указатель или ссылка на производный класс могут быть автоматически преобразованы в указатель или ссылку на базовый класс (см. раздел 15.2.2 и раздел 15.5). Это справедливо и для множественного наследования. Указатель или ссылка на производный класс могут быть преобразованы в указатель или ссылку на любой из его базовых классов. Например, указатель или ссылка на класс ZooAnimal, Bear или Endangered может указывать или ссылаться на объект класса Panda.

// функции, получающие ссылки на класс, базовый для класса Panda

void print(const Bear&);

void highlight(const Endangered&);

ostream& operator<<(ostream&, const ZooAnimal&);

Panda ying_yang("ying_yang");

print(ying_yang);          // передает объект класса Panda как

                           // ссылку на объект класса Bear

highlight(ying_yang);      // передает объект класса Panda как

                           // ссылку на объект класса Endangered

cout << ying_yang << endl; // передает объект класса Panda как

                           // ссылку на объект класса ZooAnimal

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

void print(const Bear&);

void print(const Endangered&);

Вызов функции print() без квалификации для объекта класса Panda приведет к ошибке во время выполнения.

Panda ying_yang("ying_yang");

print(ying_yang); // ошибка: неоднозначность

Поиск на основании типа указателя или ссылки

Как и при одиночном наследовании, статический тип объекта, указателя или ссылки определяет, какие из членов можно использовать. Если используется указатель класса ZooAnimal, для применения будут пригодны только те функции, которые определены в этом классе. Части интерфейса класса Panda, специфические для классов Bear, Panda и Endangered, окажутся недоступны. Аналогично указатель или ссылка на класс Bear применимы только для доступа к членам классов Bear и ZooAnimal, а указатель или ссылка на класс Endangered ограничены лишь членами класса Endangered.

В качестве примера рассмотрим следующие вызовы с учетом того, что эти классы определяют виртуальные функции, перечисленные в табл. 18.1.

Перейти на страницу:

Похожие книги