首先指定创建Query对象的操作符为友元。
在Query类的 public接口中,声明了但没有定义接受string对象的构造函数,该构造函数创建WordQuery对象,因此在定义WordQuery类之前不能定义它。
后面三个成员处理复制控制,与Sales_item类中的对应成员相同。
最后两个public成员表示对Query_base类的接口。每种情况下,Query操作都使用它的Query_base指针调用相应Query_base操作。这些操作是虚函数,在运行时根据q指向的对象的类型确定调用的实际版本。
Query类实现的private部分包括一个接受Query_base对象指针的构造函数,该构造函数将获得的指针存储在q中并分配新的使用计数,将使用计数初始化为1。该构造函数为private,是因为我们不希望普通用户代码定义Query_base对象,相反,创建Query对象的操作符需要这个构造函数。构造函数为private,所以必须将操作符设为友元。
2、Query重载操作符
|、&和 ~操作符分别创建OrQuery、AndQuery和 NotQuery对象:
inline
Query operator&(const Query &lhs,const Query &rhs)
{
return new AndQuery(lhs,rhs);
}
inline
Query operator|(const Query &lhs,const Query &rhs)
{
return new OrQuery(lhs,rhs);
}
inline
Query operator~(const Query &oper)
{
return new NotQuery(oper);
}
每个操作动态分配Query_base派生类型的新对象,return语句(隐式)使用接受Query_base指针的Query构造函数:
Query(Query_base*query):q(query),use(new std::size_t(1)) {}
用操作分配的Query_base指针创建Query对象。例如,~操作符中的return语句等价于:
Query_base *tmp = new NotQuery(oper);
return Query(tmp);
没有操作符创建WordQuery对象,相反,为Query类定义一个接受string对象的构造函数,该构造函数生成WordQuery对象查找给定string。
3、Query输出操作符
我们希望用户可以用标准(重载的)输出操作符打印Query对象,但是,也需要打印操作是虚函数――打印 Query对象应打印Query对象指向的Query_base对象。这里存在一个问题:只有成员函数可以为虚函数,但输出操作符不能是Query_base类的成员。
要获得必要的虚函数行为,Query_base类定义了一个虚函数成员display,Query 输出操作符将使用它:
inline std::ostream &
operator<<(std::ostream &os,const Query &q)
{
return q.display(os);
}
如果编写:
Query andq = Query(sought1) & Query(sought2);
cout << "\nExecuted query: " << andq << endl;
将调用display的WordQuery实例。更一般的,以下代码:
Query query = some_query;
cout << query << endl;
将调用程序运行到此时与query所指对象相关联的display实例。
五、派生类
下面要实现具体的查询类:
WordQuery类最直接,它的工作是保存要查找的单词。
其他类操作一个或两个Query操作数。NotQuery对象对别的Query对象的结果求反,AndQuery类和 OrQuery类都有两个操作数,操作数实际存储在它们的公共基类BinaryQuery中。
在这些类当中,操作数都可以是任意具体Query_base类的对象:NotQuery 对象可以应用于WordQuery对象、AndQuery对象、OrQuery对象或其他NotQuery对象。要允许这种灵活性,操作数必须存储为Query_base指针,它可以指向任意具体的Query_base类。
但是,我们的类不存储Query_base指针,而是使用自己的Query句柄。正如使用句柄可以简化用户代码,也可以使用同样的句柄类简化类代码。将Query操作数设为const,因为一旦创立了Query_base对象,就没有操作可以改变操作数了。
了解了这些类的设计之后,就可以实现它们了。
1、WordQuery类
WordQuery是一种Query_base,它在给定的查询映射中查找指定的单词:
class WordQuery : public Query_base
{
friend class Query; //只有Query句柄能够创建WordQuery对象
WordQuery(const std::string &s):query_word(s){}
std::set
eva l(const TextQuery &t) const
{
return t.run_query(query_word);
}
std::ostream &display(std::ostream &os) const
{
return os << query_word;
}
std::string query_word;
};
像Query_base类一样,WordQuery类没有 public成员,WordQuery必须将Query类设为友元以允许Query访问WordQuery构造函数。
每个具体的查询类必须定义继承的纯虚函数。WordQuery类的操作足够简单,可以定义在类定义中。eva l成员调用其TextQuery形参的run_query成员[此处原书与翻译有误,英文原版为query_text,第四版翻译为query_word,特注于此!],将用于创建该WordQuery对象的 string对象传给它。要display一个 WordQuery对象,就打印query_word对象。
2、NotQuery类
class NotQuery : public Query_base
{
friend Query operator~(const Query &);
NotQuery(Query q):query(q){}
std::set
eva l(const TextQuery &) const;
std::ostream &display(std::ostream &os) const
{
return os << "~(" << query << ")";
}
const Query query;
};
将Query的重载 ~操作符设为友元,从而允许该操作符创建新的NotQuery对象。为了display一个 NotQuery对象,打印~对象,将输出用圆括号括住以保证读者清楚优先级。
【注解】
display操作中输出操作符的使用最终是对Query_base对象的虚函数调用:
std::ostream &display(std::ostream &os) const
{
return os << "~(" << query << ")";
}
eva l成员比较复杂,我们将在类定义体之外实现它,eva l函数将在后面介绍。
3、BinaryQuery类
BinaryQuery类是一个抽象类,保存AndQuery和Or