C++ Primer 学习笔记_73_面向对象编程 --再谈文本查询示例(四)

2014-11-24 12:57:15 · 作者: · 浏览: 7
sr_use() { if (-- *use == 0) { delete q; delete use; } } };

首先指定创建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