На следующем этапе переопределим функцию add_item() так, чтобы она получала объект класса Quote вместо указателя shared_ptr. Эта новая версия функции add_item() отработает резервирование памяти так, чтобы пользователи больше не должны были делать это сами. Определим две ее версии: одна будет копировать переданный ей объект, а другая перемещать его (см. раздел 13.6.3):

void add_item(const Quote& sale); // копирует переданный объект

void add_item(Quote&& sale);      // перемещает переданный объект

Единственная проблема в том, что функция add_item() не знает, какой тип резервировать. При резервировании памяти функция add_item() скопирует (или переместит) свой параметр sale. Выражение new будет выглядеть примерно так:

new Quote(sale)

К сожалению, это выражение будет неправильным: оператор new резервирует объект запрошенного типа. Оно резервирует объект типа Quote и копирует часть Quote параметра sale. Но если переданный параметру sale объект будет иметь тип Bulk_quote, то он будет усечен.

Имитация виртуального копирования

Эту проблему можно решить, снабдив класс Quote виртуальной функцией-членом, резервирующей его копию.

class Quote {

 public:

 // виртуальная функция, возвращающая динамически созданную копию

 // эти члены используют квалификаторы ссылки; раздел 13.6.3

 virtual Quote* clone() const & {return new Quote(*this);}

 virtual Quote* clone() &&

  {return new Quote(std::move(*this));}

 // другие члены как прежде

};

class Bulk_quote : public Quote {

 Bulk_quote* clone() const & {return new Bulk_quote(*this);}

 Bulk_quote* clone() &&

  {return new Bulk_quote(std::move(*this));}

 // другие члены, как прежде

};

Поскольку функция add_item() имеет версии копирования и перемещения, были определены версии l- и r-значения функции clone() (см. раздел 13.6.3). Каждая функция clone() резервирует новый объект ее собственного типа. Функция-член константной ссылки на l-значение копирует себя во вновь зарезервированный объект; функция-член ссылки на r-значение перемещает свои данные.

Используя функцию clone(), довольно просто написать новые версии функции add_item():

class Basket {

public:

 void add_item(const Quote& sale) // копирует переданный объект

  { items.insert(std::shared_ptr(sale.clone())); }

 void add_item(Quote&& sale)      // перемещает переданный объект

  { items.insert(

     std::shared_ptr(std::move(sale).clone())); }

 // другие члены, как прежде

};

Как и сама функция add_item(), функция clone() перегружается на основании того, вызвана ли она для l- или r-значения. Таким образом, первая версия функции add_item() вызывает константную версию l-значения функции clone(), а вторая версия вызывает версию ссылки на r-значение. Обратите внимание, что хотя в версии r-значения типом параметра sale является ссылка на r-значение, сам параметр sale (как и любая другая переменная) является l-значением (см. раздел 13.6.1). Поэтому для привязки ссылки на r-значение к параметру sale вызывается функция move().

Наша функция clone() является также виртуальной. Будет ли выполнена функция из класса Quote или Bulk_quote, зависит (как обычно) от динамического типа параметра sale. Независимо от того, копируются или перемещаются данные, функция clone() возвращает указатель на вновь зарезервированный объект его собственного типа. С этим объектом связывается указатель shared_ptr, и вызывается функция insert() для добавления этого вновь зарезервированного объекта к items. Обратите внимание: так как указатель shared_ptr поддерживает преобразование производного класса в базовый (см. раздел 15.2.2), указатель shared_ptr можно привязать к Bulk_quote*.

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

Упражнение 15.30. Напишите собственную версию класса Basket и используйте ее для вычисления цены за те же транзакции, что и в предыдущих упражнениях.

<p>15.9. Возвращаясь к запросам текста</p>
Перейти на страницу:

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