Категории
Самые читаемые
Лучшие книги » Компьютеры и Интернет » Программирование » Язык программирования C++. Пятое издание - Стенли Липпман

Язык программирования C++. Пятое издание - Стенли Липпман

Читать онлайн Язык программирования C++. Пятое издание - Стенли Липпман

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 266 267 268 269 270 271 272 273 274 ... 297
Перейти на страницу:

Как и при одиночным наследовании, список наследования может включить только те классы, которые были определены и не были определены как final (см. раздел 15.2.2). Язык С++ не налагает никаких ограничений на количество базовых классов, из которых может быть получен производный класс. Однако базовый класс может присутствовать в списке наследования только один раз.

При множественном наследовании классы наследуют состояние каждого из базовых классов

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

Рис. 18.2. Концептуальная структура объекта класса Panda

Конструкторы производного класса инициализируют все объекты базовых классов

Создание объекта производного класса подразумевает создание и инициализацию внутренних объектов всех его базовых классов. В случае одиночного наследования из (единого) базового класса (см. раздел 15.2.2) в списке инициализации конструктора производного класса можно передать значения только для прямых базовых классов:

// явная инициализация объектов обоих базовых классов

Panda::Panda(std::string name, bool onExhibit)

 : Bear(name, onExhibit, "Panda"),

   Endangered(Endangered::critical) { }

// неявное применение стандартного конструктора класса Bear для

// инициализации его внутреннего объекта

Panda::Panda()

 : Endangered(Endangered::critical) { }

Список инициализации конструктора позволяет передать аргументы каждому из прямых базовых классов, однако порядок выполнения конструкторов (constructor order) зависит от порядка их расположения в списке наследования класса. Порядок их расположения в списке инициализации конструктора не имеет значения. Объект класса Panda инициализируется следующим образом.

• Внутренний объект класса ZooAnimal, самого первого базового класса иерархии класса Panda, непосредственного базового для класса Bear создается первым.

• Внутренний объект класса Bear, первого непосредственного базового класса для класса Panda, инициализируется следующим.

• Внутренний объект класса Endangered, второго непосредственного базового класса для класса Panda, инициализируется следующим.

• Последней инициализируется наиболее производная часть класса Panda.

Унаследованные конструкторы и множественное наследование

По новому стандарту производный класс может наследовать свои конструкторы от одного или нескольких своих базовых классов (см. раздел 15.7.4). Нельзя наследовать тот же конструктор (т.е. конструктор с тем же списком параметров) от более чем одного базового класса:

struct Base1 {

 Base1() = default;

 Base1(const std::string&);

 Base1(std::shared_ptr<int>);

};

struct Base2 {

 Base2() = default;

 Base2(const std::string&);

 Base2(int);

};

// ошибка: D1 пытается унаследовать D1::D1(const string&) от обоих

// базовых классов

struct D1: public Base1, public Base2 {

 using Base1::Base1; // наследует конструкторы от Base1

 using Base2::Base2; // наследует конструкторы от Base2

};

Класс, унаследовавший тот же конструктор от нескольких базовых классов, должен определить собственную версию этого конструктора:

struct D2: public Base1, public Base2 {

 using Base1::Base1; // наследует конструкторы от Base1

 using Base2::Base2; // наследует конструкторы от Base2

 // D2 должен определить собственный конструктор, получающий string

 D2(const string &s) : Base1(s), Base2(s) { }

 D2() = default; // необходимо, поскольку D2 определяет собственный

                 // конструктор

};

Деструкторы и множественное наследование

Как обычно, деструктор в производном классе отвечает за освобождение ресурсов, зарезервированных этим классом. Автоматически освобождаются члены только производного класса и всех базовых классов. Тело синтезируемого деструктора пусто.

Деструкторы всегда выполняются в порядке, обратном вызову конструкторов. В данном примере порядок вызова деструкторов будет следующим: ~Panda(), ~Endangered(), ~Bear(), ~ZooAnimal().

Функции копирования и перемещения при множественном наследовании

Как и в случае одиночного наследования, классы с несколькими базовыми классами, определяющими собственные конструкторы копирования, перемещения и операторы присвоения, должны копировать, перемещать и присваивать весь объект (см. раздел 15.7.2). Базовые части класса, производного от нескольких базовых, автоматически копируются, перемещаются и присваиваются, только если производный класс использует синтезируемые версии этих функций-членов. В синтезируемых функциях-членах управления копированием каждый базовый класс неявно создается, присваивается или удаляется с использованием соответствующего члена базового класса.

Например, если класс 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;

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

При одиночном наследовании указатель или ссылка на производный класс могут быть автоматически преобразованы в указатель или ссылку на базовый класс (см. раздел 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 приведет к ошибке во время выполнения.

1 ... 266 267 268 269 270 271 272 273 274 ... 297
Перейти на страницу:
На этой странице вы можете бесплатно скачать Язык программирования C++. Пятое издание - Стенли Липпман торрент бесплатно.
Комментарии