在这种情况下,句柄将完全屏蔽基础继承层次,用户将只能间接地通过Query对象的操作创建和操纵Query_base对象。我们将定义Query对象的三个重载操作符以及Query构造函数,Query构造函数将动态分配新的Query_base对象。每个操作符将生成的对象绑定到Query句柄:
1)&操作符将生成绑定到新的AndQuery对象的 Query对象;
2)|操作符将生成绑定到新的OrQuery对象的 Query对象;
3)~操作符将生成绑定到新的NotQuery对象的 Query对象。
4)给Query定义一个参数为string对象的构造函数,该构造函数将生成新的WordQuery。
Query类将提供与Query_base类同样的操作:eva l 对相关查询进行计算,display 打印查询。它将定义重载输出操作符显示相关查询。
查询程序设计:扼要重述 |
|
|---|---|
操作或表达式 |
功能 |
TextQuery |
读指定文件并创建相关查找映射的类。该类提供query_text操作,该操作接受string实参并返回一个set,保存出现实参的行的编号 |
Query_base |
查询类的抽象基类 |
Query |
用于计数的句柄类,它指向Query_base派生类的对象 |
WordQuery |
从Query_base派生的类,查找给定单词 |
NotQuery |
从Query_base派生的类,返回操作数不出现的行的编号的集合 |
BinaryQuery |
从Query_base派生的抽象基类类型,表示带两个Query操作数的查询 |
OrQuery |
从BinaryQuery派生的类,返回两个操作数出现的行编号集的并集 |
AndQuery |
从BinaryQuery派生的类,返回两个操作数出现的行编号集的交集 |
q1& q2 |
返回Query对象,该Query对象绑定到保存q1和q2的新AndQuery对象 |
q1| q2 |
返回Query对象,该Query对象绑定到保存q1和q2的新OrQuery对象 |
~q |
返回Query对象,该Query对象绑定到保存q的新NotQuery对象 |
Queryq(s) |
将Queryq绑定到保存strings的新的WordQuery对象 |
我们的设计:扼要重述
【小心地雷】
理解设计经常是最困难的部分,尤其是刚开始设计面向对象系统时。一旦熟悉了设计,实现就是顺理成章的了!
这个应用程序的主要工作由建立对象表示用户的查询构成,认识到这一点很重要。如下图所示,表达式:
Query q = Query("fiery") & Query("bird") | Query("wind");
生成10个对象:5个Query_base对象及其相关联的句柄。5个Query_base对象分别为:3个WordQuery对象,一个OrQuery对象和AndQuery对象。

一旦建立了对象树,计算(或显示)给定查询基本上就是沿着这些链接,要求树中每个对象计算(或显示)自己的过程,该过程由编译器管理。
例如,如果调用 q (即,在这棵树的树根)的eva l,则 eva l 将要求 q 指向的 OrQuery 对 象调用 eva l 来计算自己,计算这个 OrQuery对象用两个操作数调用 eva l,这个依次调用 AndQuery 对象和 WordQuery 对象的 eva l,查找单词 wind,依此 类推。
三、Query_base类
首先定义Query_base类:
class Query_base
{
friend class Query;
protected:
typedef TextQuery::line_no line_no;
virtual ~Query_base() {}
private:
virtual std::set
eva l(const TextQuery &) const = 0;
virtual std::ostream &
display(std::ostream & = std::cout) const = 0;
};
这个类定义了两个接口成员:eva l和display。两个成员都是纯虚函数,因此该类为抽象类,应用程序中将没有Query_base类型的对象。
用户和派生类将只通过Query句柄使用Query_base类,因此,将Query_base接口设为private。(虚)析构函数和类型别名为 protected,这些派生类型就能够访问这些成员,构造函数由派生类构造函数(隐式)使用,因此派生类必须能够访问构造函数。
给Query句柄类授予友元关系,该类的成员将调用Query_base中的虚函数因此必须能够访问它们。
四、Query句柄类
Query句柄将类似于Sales_item类,因为它将保存Query_base指针和使用计数指针。像Sales_item类一样,Query的复制控制成员将管理使用计数和Query_base指针。
与Sales_item不同的是,Query类将只为Query_base继承层次提供接口。用户将不能直接访问Query或其派生类的任意成员。这一设计决定导致Query和 Sales_item之间存在两个区别:
第一个区别是,Query类将不定义解引用操作符和箭头操作符的重载版本。Query_base类没有 public成员,如果Query句柄定义了解引用操作符和箭头操作符,它们将没有用处!使用那些操作符访问成员的任何尝试都将失败,相反,Query类必须定义接口函数eva l和display的自身版本。
另一个区别来自于我们打算怎样创建继承层次的对象。我们的设计指出将只通过 Query句柄的操作创建Query_base的派生类对象,这个区别导致Query类需要与Sales_item句柄中所用的构造函数不同的构造函数。
1、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 &);
Query(const Query &c):q(c.q),use(c.use)
{
++ *use;
}
~Query()
{
desr_use();
}
Query &operator=(const Query &);
std::set
eva l(const TextQuery &t) const
{
return q -> eva l(t);
}
std::ostream &display(std::ostream &os = std::cout)
{
return q -> display(os);
}
private:
Query(Query_base *query):q(query),use(new std::size_t(1)) {}
Query_base *q;
std::size_t *use;
void de