第十五章 面向对象程序设计
练习15.1
什么是虚成员?
解:
对于某些函数,基类希望它的派生类各自定义适合自身的版本,此时基类就将这些函数声明成虚函数。
练习15.2
protected访问说明符与private有何区别?
解:
protected: 基类和和其派生类还有友元可以访问。private: 只有基类本身和友元可以访问。
练习15.3
定义你自己的
Quote类和print_total函数。
解:
Quote:
#include <string>class Quote{public:Quote() = default;Quote(const std::string &b, double p) :bookNo(b), price(p){}std::string isbn() const { return bookNo; }virtual double net_price(std::size_t n) const { return n * price; }virtual ~Quote() = default;private:std::string bookNo;protected:double price = 0.0;};
主函数:
#include "ex_15_3.h"#include <iostream>#include <string>#include <map>#include <functional>double print_total(std::ostream& os, const Quote& item, size_t n);int main(){return 0;}double print_total(std::ostream &os, const Quote &item, size_t n){double ret = item.net_price(n);os << "ISBN:" << item.isbn()<< "# sold: " << n << " total due: " << ret << std::endl;return ret;}
练习15.4
下面哪条声明语句是不正确的?请解释原因。
class Base { ... };(a) class Derived : public Derived { ... };(b) class Derived : private Base { ... };(c) class Derived : public Base;
解:
- (a) 不正确。类不能派生自身。
- (b) 不正确。这是定义而非声明。
- (c) 不正确。派生列表不能出现在这。
练习15.5
定义你自己的
Bulk_quote类。
解:
#include "ex_15_3.h"class Bulk_quote : public Quote{public:Bulk_quote() = default;Bulk_quote(const std::string& b, double p, std::size_t q, double disc) :Quote(b, p), min_qty(q), discount(disc) {}double net_price(std::size_t n) const override;private:std::size_t min_qty = 0;double discount = 0.0;};
练习15.6
将
Quote和Bulk_quote的对象传给15.2.1节练习中的print_total函数,检查该函数是否正确。
解:
#include "ex_15_3.h"#include "ex_15_5.h"#include <iostream>#include <string>double print_total(std::ostream& os, const Quote& item, size_t n);int main(){// ex15.6Quote q("textbook", 10.60);Bulk_quote bq("textbook", 10.60, 10, 0.3);print_total(std::cout, q, 12);print_total(std::cout, bq, 12);return 0;}double print_total(std::ostream &os, const Quote &item, size_t n){double ret = item.net_price(n);os << "ISBN:" << item.isbn()<< "# sold: " << n << " total due: " << ret << std::endl;return ret;}
练习15.7
定义一个类使其实现一种数量受限的折扣策略,具体策略是:当购买书籍的数量不超过一个给定的限量时享受折扣,如果购买量一旦超过了限量,则超出的部分将以原价销售。
解:
#include "ex_15_5.h"class Limit_quote : public Quote{public:Limit_quote();Limit_quote(const std::string& b, double p, std::size_t max, double disc) :Quote(b, p), max_qty(max), discount(disc){}double net_price(std::size_t n) const override;private:std::size_t max_qty = 0;double discount = 0.0;};double Limit_quote::net_price(std::size_t n) const{if (n > max_qty)return max_qty * price * discount + (n - max_qty) * price;elsereturn n * discount *price;}
练习15.8
给出静态类型和动态类型的定义。
解:
表达式的静态类型在编译时总是已知的,它是变量声明时的类型或表达式生成的类型。动态类型则是变量或表达式表示的内存中的对象的类型。动态类型直到运行时才可知。
练习15.9
在什么情况下表达式的静态类型可能与动态类型不同?请给出三个静态类型与动态类型不同的例子。
解:
基类的指针或引用的静态类型可能与其动态类型不一致。
练习15.10
回忆我们在8.1节进行的讨论,解释第284页中将
ifstream传递给Sales_data的read函数的程序是如何工作的。
解:
std::ifstream 是 std::istream 的派生基类,因此 read 函数能够正常工作。
练习15.11
为你的
Quote类体系添加一个名为debug的虚函数,令其分别显示每个类的数据成员。
解:
void Quote::debug() const{std::cout << "data members of this class:\n"<< "bookNo= " <<this->bookNo << " "<< "price= " <<this->price<< " ";}
练习15.12
有必要将一个成员函数同时声明成
override和final吗?为什么?
解:
有必要。override 的含义是重写基类中相同名称的虚函数,final 是阻止它的派生类重写当前虚函数。
练习15.13
给定下面的类,解释每个
class base {public:string name() { return basename;}virtual void print(ostream &os) { os << basename; }private:string basename;};class derived : public base {public:void print(ostream &os) { print(os); os << " " << i; }private:int i;};
在上述代码中存在问题吗?如果有,你该如何修改它?
解:
有问题。应该改为:
void print(ostream &os) override { base::print(os); os << " derived\n " << i; }
练习15.14
给定上一题中的类以及下面这些对象,说明在运行时调用哪个函数:
base bobj; base *bp1 = &bobj; base &br1 = bobj;derived dobj; base *bp2 = &dobj; base &br2 = dobj;(a) bobj.print(); (b)dobj.print(); (c)bp1->name();(d)bp2->name(); (e)br1.print(); (f)br2.print();
解:
- (a) 编译时。
- (b) 编译时。
- (c) 编译时。
- (d) 编译时。
- (e) 运行时。
base::print() - (f) 运行时。
derived::print()
练习15.15
定义你自己的
Disc_quote和Bulk_quote。
解:
Disc_quote:
#include "quote.h"class Disc_quote : public Quote{public:Disc_quote();Disc_quote(const std::string& b, double p, std::size_t q, double d) :Quote(b, p), quantity(q), discount(d) { }virtual double net_price(std::size_t n) const override = 0;protected:std::size_t quantity;double discount;};
Bulk_quote:
#include "disc_quote.h"class Bulk_quote : public Disc_quote{public:Bulk_quote() = default;Bulk_quote(const std::string& b, double p, std::size_t q, double disc) :Disc_quote(b, p, q, disc) { }double net_price(std::size_t n) const override;void debug() const override;};
练习15.16
改写你在15.2.2节练习中编写的数量受限的折扣策略,令其继承
Disc_quote。
解:
Limit_quote:
#include "disc_quote.h"class Limit_quote : public Disc_quote{public:Limit_quote() = default;Limit_quote(const std::string& b, double p, std::size_t max, double disc):Disc_quote(b, p, max, disc) { }double net_price(std::size_t n) const override{ return n * price * (n < quantity ? 1 - discount : 1 ); }void debug() const override;};
练习15.17
尝试定义一个
Disc_quote的对象,看看编译器给出的错误信息是什么?
解:
error: cannot declare variable 'd' to be of abstract type 'Disc_quote': Disc_quote d;
练习15.18
假设给定了第543页和第544页的类,同时已知每个对象的类型如注释所示,判断下面的哪些赋值语句是合法的。解释那些不合法的语句为什么不被允许:
Base *p = &d1; //d1 的类型是 Pub_Dervp = &d2; //d2 的类型是 Priv_Dervp = &d3; //d3 的类型是 Prot_Dervp = &dd1; //dd1 的类型是 Derived_from_Publicp = &dd2; //dd2 的类型是 Derived_from_Privatep = &dd3; //dd3 的类型是 Derived_from_Protected
解:
Base *p = &d1; 合法p = &d2; 不合法p = &d3; 不合法p = &dd1; 合法p = &dd2; 不合法p = &dd3; 不合法
只有在派生类是使用public的方式继承基类时,用户代码才可以使用派生类到基类(derived-to-base)的转换。
练习15.19
假设543页和544页的每个类都有如下形式的成员函数:
void memfcn(Base &b) { b = *this; }
对于每个类,分别判断上面的函数是否合法。
解:
合法:
- Pub_Derv
- Priv_Derv
- Prot_Derv
- Derived_from_Public
- Derived_from_Protected
不合法: - Derived_from_Private
这段代码是在成员函数中使用Base。Priv_Drev中的Base部分虽然是private的,但其成员函数依然可以访问;Derived_from_Private继承自Priv_Drev,不能访问Priv_Drev中的private成员,因此不合法。
练习15.20
编写代码检验你对前面两题的回答是否正确。
解:
#include <iostream>#include <string>#include "exercise15_5.h"#include "bulk_quote.h"#include "limit_quote.h"#include "disc_quote.h"class Base{public:void pub_mem(); // public memberprotected:int prot_mem; // protected memberprivate:char priv_mem; // private member};struct Pub_Derv : public Base{void memfcn(Base &b) { b = *this; }};struct Priv_Derv : private Base{void memfcn(Base &b) { b = *this; }};struct Prot_Derv : protected Base{void memfcn(Base &b) { b = *this; }};struct Derived_from_Public : public Pub_Derv{void memfcn(Base &b) { b = *this; }};struct Derived_from_Private : public Priv_Derv{//void memfcn(Base &b) { b = *this; }};struct Derived_from_Protected : public Prot_Derv{void memfcn(Base &b) { b = *this; }};int main(){Pub_Derv d1;Base *p = &d1;Priv_Derv d2;//p = &d2;Prot_Derv d3;//p = &d3;Derived_from_Public dd1;p = &dd1;Derived_from_Private dd2;//p =& dd2;Derived_from_Protected dd3;//p = &dd3;return 0;}
练习15.21
从下面这些一般性抽象概念中任选一个(或者选一个你自己的),将其对应的一组类型组织成一个继承体系:
(a) 图形文件格式(如gif、tiff、jpeg、bmp)(b) 图形基元(如方格、圆、球、圆锥)(c) C++语言中的类型(如类、函数、成员函数)
解:
#include <iostream>#include <string>#include "quote.h"#include "bulk_quote.h"#include "limit_quote.h"#include "disc_quote.h"// just for 2D shapeclass Shape{public:typedef std::pair<double, double> Coordinate;Shape() = default;Shape(const std::string& n) :name(n) { }virtual double area() const = 0;virtual double perimeter() const = 0;virtual ~Shape() = default;private:std::string name;};class Rectangle : public Shape{public:Rectangle() = default;Rectangle(const std::string& n,const Coordinate& a,const Coordinate& b,const Coordinate& c,const Coordinate& d) :Shape(n), a(a), b(b), c(c), d(d) { }~Rectangle() = default;protected:Coordinate a;Coordinate b;Coordinate c;Coordinate d;};class Square : public Rectangle{public:Square() = default;Square(const std::string& n,const Coordinate& a,const Coordinate& b,const Coordinate& c,const Coordinate& d) :Rectangle(n, a, b, c, d) { }~Square() = default;};int main(){return 0;}
练习15.22
对于你在上一题中选择的类,为其添加函数的虚函数及公有成员和受保护的成员。
解:
参考15.21。
练习15.23
假设第550页的
D1类需要覆盖它继承而来的fcn函数,你应该如何对其进行修改?如果你修改之后fcn匹配了Base中的定义,则该节的那些调用语句将如何解析?
解:
移除 int 参数。
练习15.24
哪种类需要虚析构函数?虚析构函数必须执行什么样的操作?
解:
基类通常应该定义一个虚析构函数。
练习15.25
我们为什么为
Disc_quote定义一个默认构造函数?如果去掉该构造函数的话会对Bulk_quote的行为产生什么影响?
解:
因为Disc_quote的默认构造函数会运行Quote的默认构造函数,而Quote默认构造函数会完成成员的初始化工作。
如果去除掉该构造函数的话,Bulk_quote的默认构造函数而无法完成Disc_quote的初始化工作。
练习15.26
定义
Quote和Bulk_quote的拷贝控制成员,令其与合成的版本行为一致。为这些成员以及其他构造函数添加打印状态的语句,使得我们能够知道正在运行哪个程序。使用这些类编写程序,预测程序将创建和销毁哪些对象。重复实验,不断比较你的预测和实际输出结果是否相同,直到预测完全准确再结束。
解:
Quote:
#include <string>#include <iostream>class Quote{friend bool operator !=(const Quote& lhs, const Quote& rhs);public:Quote() { std::cout << "default constructing Quote\n"; }Quote(const std::string &b, double p) :bookNo(b), price(p){std::cout << "Quote : constructor taking 2 parameters\n";}// copy constructorQuote(const Quote& q) : bookNo(q.bookNo), price(q.price){std::cout << "Quote: copy constructing\n";}// move constructorQuote(Quote&& q) noexcept : bookNo(std::move(q.bookNo)), price(std::move(q.price)){ std::cout << "Quote: move constructing\n"; }// copy =Quote& operator =(const Quote& rhs){if (*this != rhs){bookNo = rhs.bookNo;price = rhs.price;}std::cout << "Quote: copy =() \n";return *this;}// move =Quote& operator =(Quote&& rhs) noexcept{if (*this != rhs){bookNo = std::move(rhs.bookNo);price = std::move(rhs.price);}std::cout << "Quote: move =!!!!!!!!! \n";return *this;}std::string isbn() const { return bookNo; }virtual double net_price(std::size_t n) const { return n * price; }virtual void debug() const;virtual ~Quote(){std::cout << "destructing Quote\n";}private:std::string bookNo;protected:double price = 10.0;};bool inlineoperator !=(const Quote& lhs, const Quote& rhs){return lhs.bookNo != rhs.bookNo&&lhs.price != rhs.price;}
Bulk_quote:
#include "Disc_quote.h"#include <iostream>class Bulk_quote : public Disc_quote{public:Bulk_quote() { std::cout << "default constructing Bulk_quote\n"; }Bulk_quote(const std::string& b, double p, std::size_t q, double disc) :Disc_quote(b, p, q, disc){std::cout << "Bulk_quote : constructor taking 4 parameters\n";}// copy constructorBulk_quote(const Bulk_quote& bq) : Disc_quote(bq){std::cout << "Bulk_quote : copy constructor\n";}// move constructorBulk_quote(Bulk_quote&& bq) : Disc_quote(std::move(bq)) noexcept{std::cout << "Bulk_quote : move constructor\n";}// copy =()Bulk_quote& operator =(const Bulk_quote& rhs){Disc_quote::operator =(rhs);std::cout << "Bulk_quote : copy =()\n";return *this;}// move =()Bulk_quote& operator =(Bulk_quote&& rhs) noexcept{Disc_quote::operator =(std::move(rhs));std::cout << "Bulk_quote : move =()\n";return *this;}double net_price(std::size_t n) const override;void debug() const override;~Bulk_quote() override{std::cout << "destructing Bulk_quote\n";}};
程序输出结果:
default constructing Quotedefault constructing Disc_quotedefault constructing Bulk_quoteQuote : constructor taking 2 parametersDisc_quote : constructor taking 4 parameters.Bulk_quote : constructor taking 4 parametersQuote: copy constructingQuote: copy constructingdestructing Quotedestructing QuoteDisc_quote : move =()Bulk_quote : move =()destructing Bulk_quotedestructing Dis_quotedestructing Quotedestructing Bulk_quotedestructing Dis_quotedestructing Quote
练习15.27
重新定义你的
Bulk_quote类,令其继承构造函数。
解:
#include "disc_quote.h"#include <iostream>class Bulk_quote : public Disc_quote{public:Bulk_quote() { std::cout << "default constructing Bulk_quote\n"; }// changed the below to the inherited constructor for ex15.27.// rules: 1. only inherit from the direct base class.// 2. default, copy and move constructors can not inherit.// 3. any data members of its own are default initialized.// 4. the rest details are in the section section 15.7.4./*Bulk_quote(const std::string& b, double p, std::size_t q, double disc) :Disc_quote(b, p, q, disc) { std::cout << "Bulk_quote : constructor taking 4 parameters\n"; }*/using Disc_quote::Disc_quote;// copy constructorBulk_quote(const Bulk_quote& bq) : Disc_quote(bq){std::cout << "Bulk_quote : copy constructor\n";}// move constructorBulk_quote(Bulk_quote&& bq) : Disc_quote(std::move(bq)){std::cout << "Bulk_quote : move constructor\n";}// copy =()Bulk_quote& operator =(const Bulk_quote& rhs){Disc_quote::operator =(rhs);std::cout << "Bulk_quote : copy =()\n";return *this;}// move =()Bulk_quote& operator =(Bulk_quote&& rhs){Disc_quote::operator =(std::move(rhs));std::cout << "Bulk_quote : move =()\n";return *this;}double net_price(std::size_t n) const override;void debug() const override;~Bulk_quote() override{std::cout << "destructing Bulk_quote\n";}};
练习15.28
定义一个存放
Quote对象的vector,将Bulk_quote对象传入其中。计算vector中所有元素总的net_price。
解:
#include <iostream>#include <string>#include <vector>#include <memory>#include "quote.h"#include "bulk_quote.h"#include "limit_quote.h"#include "disc_quote.h"int main(){/*** @brief ex15.28 outcome == 9090*/std::vector<Quote> v;for (unsigned i = 1; i != 10; ++i)v.push_back(Bulk_quote("sss", i * 10.1, 10, 0.3));double total = 0;for (const auto& b : v){total += b.net_price(20);}std::cout << total << std::endl;std::cout << "======================\n\n";/*** @brief ex15.29 outccome == 6363*/std::vector<std::shared_ptr<Quote>> pv;for (unsigned i = 1; i != 10; ++i)pv.push_back(std::make_shared<Bulk_quote>(Bulk_quote("sss", i * 10.1, 10, 0.3)));double total_p = 0;for (auto p : pv){total_p += p->net_price(20);}std::cout << total_p << std::endl;return 0;}
练习15.29
再运行一次你的程序,这次传入
Quote对象的shared_ptr。如果这次计算出的总额与之前的不一致,解释为什么;如果一直,也请说明原因。
解:
因为智能指针导致了多态性的产生,所以这次计算的总额不一致。
练习15.30
编写你自己的
Basket类,用它计算上一个练习中交易记录的总价格。
解:
Basket h:
#include "quote.h"#include <set>#include <memory>// 购物篮// a basket of objects from Quote hierachy, using smart pointers.class Basket{public:// Basket使用合成的默认构造函数和拷贝控制成员// copy verisonvoid add_item(const Quote& sale){items.insert(std::shared_ptr<Quote>(sale.clone()));}// move versionvoid add_item(Quote&& sale){items.insert(std::shared_ptr<Quote>(std::move(sale).clone()));}// 打印每本书的总价和购物篮中所有书的总价double total_receipt(std::ostream& os) const;private:// function to compare needed by the multiset memberstatic bool compare(const std::shared_ptr<Quote>& lhs,const std::shared_ptr<Quote>& rhs){return lhs->isbn() < rhs->isbn();}// hold multiple quotes, ordered by the compare memberstd::multiset<std::shared_ptr<Quote>, decltype(compare)*>items{ compare };};
Basket cpp:
#include "basket.h"double Basket::total_receipt(std::ostream &os) const{double sum = 0.0; // 保存实时计算出的总价格// iter指向ISBN相同的一批元素中的第一个// upper_bound返回一个迭代器,该迭代器指向这批元素的尾后位置for (auto iter = items.cbegin(); iter != items.cend();iter = items.upper_bound(*iter))// ^^^^^^^^^^^^^^^^^^^^^^^^^^^// @note this increment moves iter to the first element with key// greater than *iter.{sum += print_total(os, **iter, items.count(*iter));} // ^^^^^^^^^^^^^ using count to fetch// the number of the same book.os << "Total Sale: " << sum << std::endl;return sum;}
main:
#include <iostream>#include <string>#include <vector>#include <memory>#include <fstream>#include "quote.h"#include "bulk_quote.h"#include "limit_quote.h"#include "disc_quote.h"#include "basket.h"int main(){Basket basket;for (unsigned i = 0; i != 10; ++i)basket.add_item(Bulk_quote("Bible", 20.6, 20, 0.3));for (unsigned i = 0; i != 10; ++i)basket.add_item(Bulk_quote("C++Primer", 30.9, 5, 0.4));for (unsigned i = 0; i != 10; ++i)basket.add_item(Quote("CLRS", 40.1));std::ofstream log("log.txt", std::ios_base::app | std::ios_base::out);basket.total_receipt(log);return 0;}
练习15.31
已知
s1、s2、s3和s4都是string,判断下面的表达式分别创建了什么样的对象:
(a) Query(s1) | Query(s2) & ~Query(s3);(b) Query(s1) | (Query(s2) & ~Query(s3));(c) (Query(s1) & (Query(s2)) | (Query(s3) & Query(s4)));
解:
(a) OrQuery, AndQuery, NotQuery, WordQuery(b) OrQuery, AndQuery, NotQuery, WordQuery(c) OrQuery, AndQuery, WordQuery
练习15.32
当一个
Query类型的对象被拷贝、移动、赋值或销毁时,将分别发生什么?
解:
- 拷贝:当被拷贝时,合成的拷贝构造函数被调用。它将拷贝两个数据成员至新的对象。而在这种情况下,数据成员是一个智能指针,当拷贝时,相应的智能指针指向相同的地址,计数器增加1.
- 移动:当移动时,合成的移动构造函数被调用。它将移动数据成员至新的对象。这时新对象的智能指针将会指向原对象的地址,而原对象的智能指针为
nullptr,新对象的智能指针的引用计数为 1。 - 赋值:合成的赋值运算符被调用,结果和拷贝的相同的。
- 销毁:合成的析构函数被调用。对象的智能指针的引用计数递减,当引用计数为 0 时,对象被销毁。
练习15.33
当一个
Query_base类型的对象被拷贝、移动赋值或销毁时,将分别发生什么?
解:
由合成的版本来控制。然而 Query_base 是一个抽象类,它的对象实际上是它的派生类对象。
练习15.34
针对图15.3构建的表达式:
(a) 例举出在处理表达式的过程中执行的所有构造函数。(b) 例举出 cout << q 所调用的 rep。(c) 例举出 q.eval() 所调用的 eval。
解:
- a:
Query q = Query("fiery") & Query("bird") | Query("wind");
Query::Query(const std::string& s)where s == “fiery”,”bird” and “wind”WordQuery::WordQuery(const std::string& s)where s == “fiery”,”bird” and “wind”AndQuery::AndQuery(const Query& left, const Query& right);BinaryQuery(const Query&l, const Query& r, std::string s);Query::Query(std::shared_ptr<Query_base> query)2timesOrQuery::OrQuery(const Query& left, const Query& right);BinaryQuery(const Query&l, const Query& r, std::string s);Query::Query(std::shared_ptr<Query_base> query)2times
- b:
query.rep()inside the operator <<().q->rep()inside the member function rep().OrQuery::rep()which is inherited fromBinaryQuery.Query::rep()forlhsandrhs:
forrhswhich is aWordQuery:WordQuery::rep()wherequery_word("wind")is returned.Forlhswhich is anAndQuery.AndQuery::rep()which is inherited fromBinaryQuery.BinaryQuer::rep(): forrhs: WordQuery::rep()where query_word(“fiery”) is returned. Forlhs: WordQuery::rep()where query_word(“bird” ) is returned.
- c:
q.eval()q->rep(): where q is a pointer toOrQuary.QueryResult eval(const TextQuery& )const override: is called but this one has not been defined yet.
练习15.35
实现
Query类和Query_base类,其中需要定义rep而无须定义eval。
解:
Query:
#ifndef QUERY_H#define QUERY_H#include <iostream>#include <string>#include <memory>#include "query_base.h"#include "queryresult.h"#include "textquery.h"#include "wordquery.h"/*** @brief interface class to manage the Query_base inheritance hierachy*/class Query{friend Query operator~(const Query&);friend Query operator|(const Query&, const Query&);friend Query operator&(const Query&, const Query&);public:// build a new WordQueryQuery(const std::string& s) : q(new WordQuery(s)){std::cout << "Query::Query(const std::string& s) where s=" + s + "\n";}// interface functions: call the corresponding Query_base operatopnsQueryResult eval(const TextQuery& t) const{return q->eval(t);}std::string rep() const{std::cout << "Query::rep() \n";return q->rep();}private:// constructor only for friendsQuery(std::shared_ptr<Query_base> query) :q(query){std::cout << "Query::Query(std::shared_ptr<Query_base> query)\n";}std::shared_ptr<Query_base> q;};inline std::ostream&operator << (std::ostream& os, const Query& query){// make a virtual call through its Query_base pointer to rep();return os << query.rep();}#endif // QUERY_H
Query_base:
#ifndef QUERY_BASE_H#define QUERY_BASE_H#include "textquery.h"#include "queryresult.h"/*** @brief abstract class acts as a base class for all concrete query types* all members are private.*/class Query_base{friend class Query;protected:using line_no = TextQuery::line_no; // used in the eval functionvirtual ~Query_base() = default;private:// returns QueryResult that matches this queryvirtual QueryResult eval(const TextQuery&) const = 0;// a string representation of this queryvirtual std::string rep() const = 0;};#endif // QUERY_BASE_H
练习15.36
在构造函数和
rep成员中添加打印语句,运行你的代码以检验你对本节第一个练习中(a)、(b)两小题的回答是否正确。
解:
Query q = Query("fiery") & Query("bird") | Query("wind");WordQuery::WordQuery(wind)Query::Query(const std::string& s) where s=windWordQuery::WordQuery(bird)Query::Query(const std::string& s) where s=birdWordQuery::WordQuery(fiery)Query::Query(const std::string& s) where s=fieryBinaryQuery::BinaryQuery() where s=&AndQuery::AndQuery()Query::Query(std::shared_ptr<Query_base> query)BinaryQuery::BinaryQuery() where s=|OrQuery::OrQueryQuery::Query(std::shared_ptr<Query_base> query)Press <RETURN> to close this window...
std::cout << q <<std::endl;Query::rep()BinaryQuery::rep()Query::rep()WodQuery::rep()Query::rep()BinaryQuery::rep()Query::rep()WodQuery::rep()Query::rep()WodQuery::rep()((fiery & bird) | wind)Press <RETURN> to close this window...
练习15.37
如果在派生类中含有
shared_ptr<Query_base>类型的成员而非Query类型的成员,则你的类需要做出怎样的改变?
解:
参考15.35。
练习15.38
下面的声明合法吗?如果不合法,请解释原因;如果合法,请指出该声明的含义。
BinaryQuery a = Query("fiery") & Query("bird");AndQuery b = Query("fiery") & Query("bird");OrQuery c = Query("fiery") & Query("bird");
解:
- 不合法。因为
BinaryQuery是抽象类。 - 不合法。
&操作返回的是一个Query对象。 - 不合法。
&操作返回的是一个Query对象。
练习15.39
实现
Query类和Query_base类,求图15.3中表达式的值并打印相关信息,验证你的程序是否正确。
练习15.40
在
OrQuery的eval函数中,如果rhs成员返回的是空集将发生什么?
解:
不会发生什么。代码如下:
std::shared_ptr<std::set<line_no>> ret_lines =std::make_shared<std::set<line_no>>(left.begin(), left.end());
如果 rhs 成员返回的是空集,在 set 当中不会添加什么。
练习15.41
重新实现你的类,这次使用指向
Query_base的内置指针而非shared_ptr。请注意,做出上述改动后你的类将不能再使用合成的拷贝控制成员。
解:
略
练习15.42
从下面的几种改进中选择一种,设计并实现它:
(a) 按句子查询并打印单词,而不再是按行打印。(b) 引入一个历史系统,用户可以按编号查阅之前的某个查询,并可以在其中添加内容或者将其余其他查询组合。(c) 允许用户对结果做出限制,比如从给定范围的行中跳出匹配的进行显示。
解:
略
TextQuery最终项目
见 cpp_source/cha5/text_query
