Тело производного класса должно включать объявления всех виртуальных функций (virtual function), которые он намеревается определить для себя. Производный класс может включить в эти функции ключевое слово virtual, но не обязательно. По причинам, рассматриваемым в разделе 15.3, новый стандарт позволяет производному классу явно указать, что функция-член предназначена для переопределения (override) унаследованной виртуальной функции. Для этого после списка ее параметров располагают ключевое слово override.

Динамическое связывание

Динамическое связывание (dynamic binding) позволяет взаимозаменяемо использовать тот же код для обработки объектов как типа Quote, так и Bulk_quote. Например, следующая функция выводит общую стоимость при покупке заданного количества экземпляров указанной книги:

// вычислить и отобразить цену за указанное количество экземпляров

// с применением всех скидок

double print_total(ostream &os,

                   const Quote &item, size_t n) {

 // в зависимости от типа, связанного с параметром item объекта,

 // вызвать функцию Quote::net_price() или Bulk_quote::net_price()

 double ret = item.net_price(n);

 os << "ISBN: " << item.isbn() // вызов Quote::isbn()

    << " # sold: " << n << " total due: " << ret << endl;

 return ret;

}

Эта функция довольно проста — она выводит результаты вызова функций isbn() и net_price() для своего параметра и возвращает значение, вычисленное вызовом функции net_price().

Однако у этой функции есть два интересных момента: по описанным в разделе 15.2.3 причинам, поскольку параметр item является ссылкой на тип Quote, эту функцию можно вызвать как для объекта класса Quote, так и для объекта класса Bulk quote. По причинам, описанным в разделе 15.2.1, поскольку функция net_price() является виртуальной, а функция print_total() вызывает ее через ссылку, выполняемая версия функции net_price() будет зависеть от типа объекта, переданного функции print_total():

// basic имеет тип Quote; bulk имеет тип Bulk_quote

print_total(cout, basic, 20); // вызов версии net_price() класса Quote

print_total(cout, bulk, 20);  // вызов версии net_price()

                              // класса Bulk_quote

Первый вызов передает функции print_total() объект класса Quote. Когда функция print_total() вызовет функцию net_price(), будет выполнена ее версия из класса Quote. В следующем вызове, где аргумент имеет тип Bulk_quote, будет выполнена версия функции net_price() из класса Bulk_quote (применяющая скидку). Поскольку решение о выполняемой версии зависит от типа аргумента, оно может быть принято до времени выполнения. Поэтому динамическое связывание иногда называют привязкой во время выполнения (run-time binding).

В языке С++ динамическое связывание происходит тогда, когда обращение к виртуальной функции осуществляется при помощи ссылки (или указателя) на базовый класс.

<p>15.2. Определение базовых и производных классов</p>

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

<p><image l:href="#reader.png"/>15.2.1. Определение базового класса</p>

Для начала завершим определение класса Quote:

class Quote {

public:

 Quote() = default; // = default см. раздел 7.1.4

 Quote(const std::string &book, double sales_price):

  bookNo(book), price(sales_price) { }

 std::string isbn() const { return bookNo; }

 // возвращает общую цену за определенное количество проданных

 // экземпляров, а различные системы скидок определяют и

 // применяют производные классы

 virtual double net_price(std::size_t n) const

  { return n * price; }

 virtual ~Quote() = default; // динамическое связывание для

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

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