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

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

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

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 210 211 212 213 214 215 216 217 218 ... 297
Перейти на страницу:

Как уже упоминалось, функция print_total() осуществляет вызов виртуальной функции net_price(), поэтому полученная цена зависит от динамического типа **iter. Функция print_total() выводит общую сумму для данной книги и возвращает вычисленную общую стоимость. Результат добавляется в переменную sum, которая выводится после завершения цикла for.

Сокрытие указателей

Пользователи класса Basket все еще должны иметь дело с динамической памятью, поскольку функция add_item() получает указатель shared_ptr. В результате пользователи вынуждены писать код так:

Basket bsk;

bsk.add_item(make_shared<Quote>("123", 45));

bsk.add_item(make_shared<Bulk_quote>("345", 45, 3, .15));

На следующем этапе переопределим функцию 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<Quote>(sale.clone())); }

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

  { items.insert(

     std::shared_ptr<Quote>(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<Quote> можно привязать к Bulk_quote*.

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

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

15.9. Возвращаясь к запросам текста

В качестве последнего примера наследования дополним приложение текстового запроса из раздела 12.3. Написанные в этом разделе классы позволят искать вхождения данного слова в файле. Дополним эту систему возможностью создавать более сложные запросы. В этих примерах запросы будут выполняться к тексту следующей истории:

Alice Emma has long flowing red hair.

Her Daddy says when the wind blows

through her hair, it looks almost alive,

like a fiery bird in flight.

A beautiful fiery bird, he tells her,

magical but untamed.

"Daddy, shush, there is no such thing,"

she tells him, at the same time wanting

him to tell her more.

Shyly, she asks, "I mean, Daddy, is there?"

Система должна поддерживать следующие запросы.

• Запросы слов находят все строки, соответствующие заданной строке:

Executing Query for: Daddy

Daddy occurs 3 times

(line 2) Her Daddy says when the wind blows

(line 7) "Daddy, shush, there is no such thing,"

(line 10) Shyly, she asks, "I mean, Daddy, is there?"

• Инверсный запрос с использованием оператора ~ возвращает строки, которые не содержат заданную строку:

Executing Query for: ~(Alice)

~(Alice) occurs 9 times

(line 2) Her Daddy says when the wind blows

(line 3) through her hair, it looks almost alive,

(line 4) like a fiery bird in flight.

...

• Запросы ИЛИ с использованием оператора | возвращают строки, содержащие любую из двух заданных строк:

Executing Query for: (hair | Alice)

(hair | Alice) occurs 2 times

(line 1) Alice Emma has long flowing red hair,

(line 3) through her hair, it looks almost alive,

• Запросы И с использованием оператора & возвращают строки, содержащие обе заданные строки:

Executing query for: (hair & Alice)

(hair & Alice) occurs 1 time

(line 1) Alice Emma has long flowing red hair.

Кроме того, нужна возможность объединить эти операторы так

fiery & bird | wind

Для обработки составных выражений, таких как в этом примере, будут использованы обычные правила приоритета С++ (см. раздел 4.1.2). Таким образом, этому запросу соответствует строка, в которой присутствуют слова fiery и bird или слово wind:

Executing Query for: ((fiery & bird) | wind)

((fiery & bird) | wind) occurs 3 times

(line 2) Her Daddy says when the wind blows

(line 4) like a fiery bird in flight.

(line 5) A beautiful fiery bird, he tells her,

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

15.9.1. Объектно-ориентированное решение

Для представления запросов на поиск слов вполне логично было бы использовать класс TextQuery (см. раздел 12.3.2), а другие классы запросов можно было бы получить как производные от этого класса.

Однако такой подход неверен. Концептуально инверсный запрос не является разновидностью запроса на поиск слова. Инверсный запрос — это скорее запрос типа "имеет" (запрос на поиск слова или любой другой тип запроса), результат которого интерпретируется негативно.

Исходя из этого можно сделать вывод, что разные виды запросов следует оформить как независимые классы, которые совместно используют общий базовый класс:

WordQuery // Daddy

NotQuery  // ~Alice

OrQuery   // hair | Alice

AndQuery  // hair & Alice

Эти классы будет иметь только две функции.

• Функция eval(), получающая объект класса TextQuery и возвращающая объект класса QueryResult. Для поиска запрошенной строки функция eval() будет использовать переданный объект класса TextQuery.

1 ... 210 211 212 213 214 215 216 217 218 ... 297
Перейти на страницу:
На этой странице вы можете бесплатно скачать Язык программирования C++. Пятое издание - Стенли Липпман торрент бесплатно.
Комментарии