Таким образом, когда в коде встречается оператор вывода, он вызывает функцию Query::rep()
объекта andq
. Функция Query::rep()
в свою очередь осуществляет виртуальный вызов через свой указатель класса Query_base
на версию функции rep()
класса Query_base
. Поскольку объект andq
указывает на объект класса AndQuery
, этот вызов выполнит функцию AndQuery::rep()
.
Упражнение 15.32. Что будет при копировании, перемещении, присвоении и удалении объекта класса Query
?
Упражнение 15.33. А объектов класса Query_base
?
15.9.3. Производные классы
Самая интересная часть классов, производных от класса Query_base
, в том, как они представляются. Класс WordQuery
проще всех. Его задача — хранение искомого слова.
Другие классы работают на одном или двух операндах. У класса NotQuery
один операнд, а у классов AndQuery
и OrQuery
— по два. Операндами в каждом из этих классов могут быть объекты любого из реальных классов, производных от класса Query_base
: NotQuery
может быть применен к WordQuery
, как и AndQuery
, OrQuery
или NotQuery
. Для обеспечения такой гибкости операнды следует хранить как указатели на класс Query_base
. Таким образом, можно привязать указатель на любой необходимый реальный класс.
Но вместо того, чтобы хранить указатель на класс Query_base
, классы будут сами использовать объект Query
. Подобно тому, как пользовательский код упрощается при использовании класса интерфейса, можно упростить код собственного класса, используя тот же класс.
Теперь, когда конструкция этих классов известна, их можно реализовать.
WordQuery
Класс WordQuery
отвечает за поиск заданной строки. Это единственная операция, которая фактически выполняет запрос для данного объекта класса TextQuery
:
class WordQuery: public Query_base {
friend class Query; //
WordQuery(const std::string &s) : query_word (s) { }
//
//
QueryResult eval(const TextQuery &t) const
{ return t.query(query_word); }
std::string rep() const { return query_word; }
std::string query_word; //
};
Подобно классу Query_base
, у класса WordQuery
нет открытых членов; он должен сделать класс Query
дружественным, чтобы позволить ему получать доступ к конструктору WordQuery()
.
Каждый из конкретных классов запроса должен определить унаследованные чистые виртуальные функции eval()
и rep()
. Обе функции определены в теле класса WordQuery
: функция eval()
вызывает функцию-член query()
своего параметра типа TextQuery
, который фактически осуществляет поиск в файле; функция rep()
возвращает строку, которую данный объект класса WordQuery
представляет (т.е. query_word
).
Определив класс WordQuery
, можно определить конструктор Query()
, получающий строку:
inline
Query::Query(const std::string &s): q(new WordQuery(s)) { }
Этот конструктор резервирует объект класса WordQuery
и инициализирует его указатель-член так, чтобы он указывал на этот недавно созданный объект.
NotQuery
и оператор ~
Оператор ~
подразумевает создание объекта класса NotQuery
, содержащего инверсный запрос:
class NotQuery: public Query_base {
friend Query operator~(const Query &);
NotQuery(const Query &q): query(q) { }
//
//
std::string rep() const {return + query.rep() + ")";}
QueryResult eval(const TextQuery&) const;
Query query;
};
inline Query operator~(const Query &operand) {
return std::shared_ptr
}
Поскольку все члены класса NotQuery
являются закрытыми, объявляем оператор ~
дружественным. Чтобы отобразить объект класса NotQuery
, следует вывести символ "~
" сопровождаемый основным запросом. Чтобы сделать приоритет очевидным для читателя, заключим запрос в скобки.