Обе функции, eval()
и rep()
, являются чистыми виртуальными, что делает класс Query_base
абстрактным базовым (см. раздел 15.4). Поскольку класс Query_base
не предназначен для пользователей и непосредственного использования в производных классах, у него нет открытых членов. Класс Query_base
будет использоваться только через объекты класса Query
. Класс предоставляет дружественные отношения классу Query
, поскольку его члены вызывают виртуальные функции класса Query_base
.
Защищенный член line_no
будет использоваться в функциях eval()
. Деструктор также будет защищен, поскольку он используется (неявно) деструкторами в производных классах.
Query
Класс Query
предоставляет интерфейс к иерархии наследования Query_base
и скрывает ее. Каждый объект класса Query
содержит указатель shared_ptr
на соответствующий объект класса Query_base
. Поскольку класс Query
— единственный интерфейс к классам иерархии Query_base
, он должен определить собственные версии функций eval()
и rep()
.
Конструктор Query()
, получающий строку, создаст новый объект класса WordQuery
и свяжет его указатель-член shared_ptr
с этим недавно созданным объектом. Операторы &
, |
и ~
создают объекты AndQuery
, OrQuery
и NotQuery
соответственно. Эти операторы возвращают объект класса Query
, связанный с созданным им объектом. Для поддержки этих операторов класс Query
нуждается в конструкторе, получающем указатель shared_ptr
на класс Query_base
и сохраняющем его. Сделаем этот конструктор закрытым, поскольку объекты класса Query_base
не предназначены для определения общим пользовательским кодом. Так как этот конструктор является закрытым, операторы следует сделать дружественными.
Исходя из приведенного выше проекта, сам класс Query
довольно прост:
//
//
class Query {
//
friend Query operator~(const Query &);
friend Query operator|(const Query&, const Query&);
friend Query operator&(const Query&, const Query&);
public:
Query(const std::string&); //
//
QueryResult eval(const TextQuery &t) const
{ return q->eval(t); }
std::string rep() const { return q->rep(); }
private:
Query(std::shared_ptr
std::shared_ptr
};
Начнем с объявления дружественных операторов, создающих объекты класса Query
. Эти операторы должны быть друзьями, чтобы использовать закрытый конструктор.
В открытом интерфейсе для класса Query
объявляется, но еще не может быть определен получающий строку конструктор. Этот конструктор создает объект класса WordQuery
, поэтому невозможно определить этот конструктор, пока не определен сам класс WordQuery
.
Два других открытых члена представляют интерфейс для класса Query_base
. В каждом случае оператор класса Query
использует свой указатель класса Query_base
для вызова соответствующей (виртуальный) функции класса Query_base
. Фактически вызываемая версия определяется во время выполнения и будет зависеть от типа объекта, на который указывает указатель q
.
Query
Оператор вывода — хороший пример того, как работает вся система запросов:
std::ostream &
operator<<(std::ostream &os, const Query &query) {
//
//
return os << query.rep();
}
При выводе объекта класса Query
оператор вывода вызывает (открытую) функцию-член rep()
класса Query
. Эта функция осуществляет виртуальный вызов через свой указатель-член функции-члена rep()
объекта, на который указывает данный объект класса Query
.
Query andq = Query(sought1) & Query(sought2);
cout << andq << endl;