item = bulk;      // вызов Quote::operator=(const Quote&)

При создании объекта item выполняется конструктор копий класса Quote. Этот конструктор знает только о переменных-членах bookNo и price. Он копирует эти члены из части Quote объекта bulk и игнорирует члены, являющиеся частью Bulk_quote объекта bulk. Аналогично при присвоении объекта bulk объекту item ему присваивается только часть Quote объекта bulk.

Поскольку часть Bulk_quote игнорируется, говорят, что она была отсечена (sliced down).

При инициализации объекта базового типа (или присвоении) объектом производного типа копируется, перемещается или присваивается только часть базового класса производного объекта. Производная часть объекта игнорируется.

Ключевая концепция. Преобразования между типами, связанными наследованием

Есть три правила преобразования связанных наследованием классов, о которых следует помнить.

• Преобразование из производного класса в базовый применимо только к указателю или ссылке.

• Нет неявного преобразования из типа базового класса в тип производного.

• При преобразовании производного в базовый член класса может быть недоступен из за спецификатора управления доступом. Доступность рассматривается в разделе 15.5.

Хотя автоматическое преобразование применимо только к указателям и ссылкам, большинство классов в иерархии наследования (явно или неявно) определяют функции-члены управления копированием (см. главу 13). В результате зачастую вполне можно копировать, перемещать и присваивать объекты производного типа объектам базового. Однако копирование, перемещение или присвоение объекта производного типа объекту базового копирует, перемещает или присваивает только члены части базового класса объекта.

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

Упражнение 15.8. Определите статический и динамический типы.

Упражнение 15.9. Когда может возникнуть отличие статического типа выражения от его динамического типа? Приведите три примера, в которых статический и динамический типы отличаются.

Упражнение 15.10. Возвращаясь к обсуждению в разделе 8.1, объясните, как работает программа из раздела 8.2.1, где функции read() класса Sales_data передавался объект ifstream.

<p><image l:href="#reader.png"/>15.3. Виртуальные функции</p>

Как уже упоминалось, в языке С++ динамическое связывание происходит при вызове виртуальной функции-члена через ссылку или указатель на тип базового класса (см. раздел 15.1). Поскольку до времени выполнения неизвестно, какая версия функции вызывается, виртуальные функции следует определять всегда. Обычно, если функция не используется, ее определение предоставлять необязательно (см. раздел 6.1.2). Однако следует определить каждую виртуальную функцию, независимо от того, будет ли она использована, поскольку у компилятора нет никакого способа определить, используется ли виртуальная функция.

Вызовы виртуальной функции могут быть распознаны во время выполнения

Когда виртуальная функция вызывается через ссылку или указатель, компилятор создает код распознавания во время выполнения (decide at run time) вызываемой функции. Вызывается та функция, которая соответствует динамическому типу объекта, связанного с этим указателем или ссылкой.

В качестве примера рассмотрим функцию print_total() из раздела 15.1. Она вызывает функцию net_price() своего параметра item типа Quote&. Поскольку параметр item — это ссылка и функция net_price() является виртуальной, какая именно из ее версий будет вызвана во время выполнения, зависит от фактического (динамического) типа аргумента, связанного с параметром item:

Quote base("0-201-82470-1", 50);

print_total(cout, base, 10);   // вызов Quote::net_price()

Bulk_quote derived("0-201-82470-1", 50, 5, .19);

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

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