Chapter15:程序实例
购物篮程序:模拟虚拷贝
1 class Basket 2 { 3 public: 4 //使用合成的默认构造函数和拷贝控制 5 void add_item(const shared_ptr<Quote> &sale) 6 { 7 items.insert(sale); 8 } 9 double total_recipt(ostream& os) const 10 { 11 double sum = 0.0; 12 for (auto iter = items.cbegin(); iter != items.cend(); iter = items.upper_bound(*iter)) 13 { 14 sum += print_total(os, **iter, items.count(*iter)); 15 } 16 } 17 private: 18 static bool compare(shared_ptr<Quote> &lhs, shared_ptr<Quote> &rhs) 19 { 20 return lhs->isbn() < rhs.isbn(); 21 } 22 multiset<shared_ptr<Quote>, decltype(compare)*> items{ compare }; 23 24 }; 25 26 //使用方法 27 Basket bsk; 28 bsk.add_item(make_shared<Quote>("123", 45)); 29 bsk.add_item(make_shared<Bulk_quote>("345", 45,3,15)); 30 31 //我们想这么使用,即:add_item负责内存的分配与管理 32 bsk.add_item(Quote("123", 45)); 33 bsk.add_item(Bulk_quote("345", 45, 3, 15)); 34 35 //问题是,我们无法通过参数的类型Quote,来得知应该分配什么样的内存,Quote or Bulk_quote? 36 //解决方法:模拟虚拷贝 37 class Quote 38 { 39 public: 40 virtual Quote* clone() const & { return new Quote(*this); } 41 virtual Quote* clone() && {return new Quote(std::move(*this)); } 42 }; 43 class Bulk_quote:public Quote 44 { 45 public: 46 virtual Bulk_quote* clone() const & { return new Bulk_quote(*this); } 47 virtual Bulk_quote* clone() && {return new Bulk_quote(std::move(*this)); } 48 }; 49 50 void add_item(const Quote &sale) 51 { 52 items.insert(shared_ptr<Quote>(sale.clone())); 53 } 54 void add_item(Quote &&sale) 55 { 56 items.insert(shared_ptr<Quote>(std::move(sale).clone())); 57 }
文本查询程序(2):允许单词的逻辑组合查询如:fiery&bird|wind
分析:
我们只需要对于TextQuery,定义operator~(),operator|(TextQuery1,TextQuery2),operator&(TextQuery1,TextQuery2)即可完成任务。
我们这样使用:TextQuery(fiery)&TextQuery(brid)|TextQuery(wind),会出现三个TextQuery对象。
逻辑上来讲,文本文档对象TextQuery只需要一个。其实,真正需要多个操作的是查询这一操作。我们现在有必要将查询这一操作分离出来。
之前的查询是一个函数[参见 Chapter12&Chapter13程序实例]:
47 //如果没有找到string,应该返回什么? 48 //我们定义一个局部static对象,它指向一个空行号set的shared_ptr,未找到单词,则返回此对象的一个拷贝 49 QueryResult TextQuery::query(const string &sought) const 50 { 51 static shared_ptr<set<line_no>> nodata(new set<line_no>); 52 //不使用下标运算符来查找,避免将单词添加到wm中 53 auto loc = wm.find(sought); 54 if (loc == wm.end()) 55 return { sought, nodata, file }; 56 else 57 return { sought, loc->second, file }; 58 }
现在我们把它分离出来,重新写作一个类Query,然后定义operator~,operator|,operator&即可。
但是书中为了演示继承,所以把操作设计成继承体系。
每个查询类只包含两个操作:
eval,接受一个TextQuery对象,返回一个QueryResult;
rep,返回基础查询的string表示形式。
具体的类设计细节参见书本。
然后使用Query类隐藏继承体系。
所以Query_base的所有函数都是私有的。(私有虚函数竟然可以被派生基类访问?Query不是NotQuery的友元,亦可以调用私有虚函数??)
1 class Query_base 2 { 3 friend class Query; 4 protected: 5 using line_no = TextQuery::line_no; 6 virtual ~Query_base() = default; 7 private: 8 virtual QueryResult eval(const TextQuery&) const = 0; 9 virtual string rep() const = 0; 10 }; 11 12 class Query 13 { 14 friend Query operator~(const Query&); 15 friend Query operator|(const Query&, const Query&); 16 friend Query operator&(const Query&, const Query&); 17 public: 18 Query(const string&);//要生成WordQuery,WordQuery定义之后再定义; 19 QueryResult eval(const TextQuery &t) const { return q->eval(t); } 20 string rep() const { return q->rep(); } 21 private: 22 Query(shared_ptr<Query_base> query) :q(query) {} 23 shared_ptr<Query_base> q; 24 }; 25 26 ostream& operator<<(ostream &os, const Query &query) 27 { 28 return os << query.rep(); 29 } 30 31 32 //Query_base的继承体系 33 class WordQuery :public Query_base 34 { 35 friend class Query; 36 WordQuery(const string& s) :query_word(s) {} 37 QueryResult eval(const TextQuery &t) const { return t.query(query_word); } 38 string rep() const { return query_word; } 39 string query_word; 40 }; 41 //注意,Query只是Query_base和WordQuery的友元 42 inline Query::Query(const string &s) 43 :q(new WordQuery(s)) {} 44 45 46 class NotQuery :public Query_base 47 { 48 friend Query operator~(const Query&); 49 NotQuery(const Query& q):query(q){} 50 string rep()const { return "~(" + query.rep() + ")"; } 51 QueryResult eval(const TextQuery&) const; 52 Query query; 53 }; 54 55 inline Query operator~(const Query &oprand) 56 { 57 return shared_ptr<Query_base>(new NotQuery(oprand)); 58 } 59 60 class BinaryQuery :public Query_base 61 { 62 //抽象基类,Query不访问,故不用friend 63 protected: 64 BinaryQuery(const Query &l, const Query &r, string s) 65 :lhs(l), rhs(r), opSym(s) {} 66 string rep() const { return "(" + lhs.rep() + " " + opSym + " " + rhs.rep() + ")"; } 67 Query lhs, rhs; 68 string opSym; 69 }; 70 71 class AndQuery :public BinaryQuery 72 { 73 friend Query operator&(const Query&, const Query&); 74 AndQuery(const Query &left, const Query &right) 75 :BinaryQuery(left, right, "&") {} 76 QueryResult eval(const TextQuery&) const; 77 }; 78 79 inline Query operator&(const Query &lhs, const Query &rhs) 80 { 81 return shared_ptr<Query_base>(new AndQuery(lhs, rhs)); 82 } 83 84 class OrQuery :public BinaryQuery 85 { 86 friend Query operator|(const Query&, const Query&); 87 OrQuery(const Query &left, const Query &right) 88 :BinaryQuery(left, right, "|") {} 89 QueryResult eval(const TextQuery&) const; 90 }; 91 92 inline Query operator|(const Query &lhs, const Query &rhs) 93 { 94 return shared_ptr<Query_base>(new OrQuery(lhs, rhs)); 95 } 96 97 QueryResult OrQuery::eval(const TextQuery &text) const 98 { 99 auto right = rhs.eval(text), left = lhs.eval(text); 100 101 auto ret_lines = make_shared<set<line_no>>(left.begin(), left.end()); 102 ret_lines->insert(right.begin(), right.end()); 103 return QueryResult(rep(), ret_lines, left.get_file()); 104 } 105 106 QueryResult AndQuery::eval(const TextQuery &text) const 107 { 108 auto right = rhs.eval(text), left = lhs.eval(text); 109 110 auto ret_lines = make_shared<set<line_no>>(); 111 set_intersection(left.begin(), left.end(), right.begin(), right.end(),inserter(*ret_lines,ret)lines->begin())); 112 return QueryResult(rep(), ret_lines, left.get_file()); 113 114 } 115 116 QueryResult NotQuery::eval(const TextQuery &text) const 117 { 118 auto result = query.eval(text); 119 auto ret_lines = make_shared<set<line_no>>(); 120 auto beg = result.begin(), end = result, end(); 121 auto sz = result.get_file()->size(); 122 for (size_t n = 0; n != sz; ++n) 123 { 124 if (beg == end || *beg != n) 125 ret_lines->insert(n); 126 else if (beg != end) 127 ++beg; 128 } 129 130 return QueryResult(rep(), ret_lines, result.get_file()); 131 }