Например, если класс 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.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;
18.3.2. Преобразования и несколько базовых классов
При одиночном наследовании указатель или ссылка на производный класс могут быть автоматически преобразованы в указатель или ссылку на базовый класс (см. раздел 15.2.2 и раздел 15.5). Это справедливо и для множественного наследования. Указатель или ссылка на производный класс могут быть преобразованы в указатель или ссылку на любой из его базовых классов. Например, указатель или ссылка на класс ZooAnimal
, Bear
или Endangered
может указывать или ссылаться на объект класса Panda
.
//
void print(const Bear&);
void highlight(const Endangered&);
ostream& operator<<(ostream&, const ZooAnimal&);
Panda ying_yang("ying_yang");
print(ying_yang); //
//
highlight(ying_yang); //
//
cout << ying_yang << endl; //
//
Компилятор даже не пытается как-то различать базовые классы. Преобразования в каждый из базовых классов происходят одинаково успешно. Рассмотрим, например, перегруженную версию функции 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.