c++ primer第五版第十五章答案

15.01 什么是虚成员?

虚成员是基类希望派生类进行覆盖的函数,在其成员前加关键字virtual,使得该成员可以实现动态绑定。

15.02 protected访问说明符与private有何区别?

private成员:即使是基类的派生类也无法直接访问。

protected成员:基类的派生类可以访问,但禁止其它用户访问。

15.03 定义你自己的Quote类和print_total函数。

// Quote类
class Quote
{
public:
    Quote() = default;
    Quote(const std::string &book, double sales_price) : bookNo(book), price(sales_price) {}
    std::string isbn() const { return bookNo; }
    // return the total sales of given number books.
    // the derived class need overwrite the method of calculate the discount.
    virtual double net_price(std::size_t n) const { return n * price; }
    virtual ~Quote() = default;
private:
    std::string bookNo;     // isbn number
protected:
    double price = 0.0;     // origin price
};
// print_total函数
double print_total(ostream& os, const Quote& item, size_t n)
{
    double ret = item.net_price(n);
    os << "ISBN: " << item.isbn() << " # sold: " << n << " total due: " << ret << endl;
    return ret;
}

15.04 下面哪条声明语句是不正确的?请解释原因。

class Base { ... };
(a) class Derived : public Derived { ... };	 // 类重复定义,不能自己继承自己
(b) class Derived : private Base { ... };	 // 这是类的定义,并不是声明
(c) class Derived : public Base;		     // 类的声明不包含类派生列表

15.05 定义你自己的Bulk_quote类。

class Bulk_quote : public Quote
{
public:
    Bulk_quote() = default;
    Bulk_quote(const std::string& book, double price, std::size_t qty, double disc) : Quote(book, price), min_qty(qty), discount(disc) {}
    double net_price(std::size_t) const override;
private:
    std::size_t min_qty = 0;    // the minimum purchase can use the discount.
    double discount;
};
double Bulk_quote::net_price(size_t cnt) const
{
    if (cnt >= min_qty) {
        return cnt * (1-discount) * price;
    }
    else {
        return cnt * price;
    }
}

15.06 将Quote和Bulk_quote的对象传给15.2.1节练习中的print_total函数,检查该函数是否正确。

double print_total(ostream& os, const Bulk_quote& item, size_t n)
{
    double ret = item.net_price(n);
    os << "ISBN: " << item.isbn() << " # sold: " << n << " total due: " << ret << endl;
    return ret;
}

15.07 定义一个类使其实现一种数量首先的折扣策略,具体策略是:当购买书籍的数量不超过一个给定的限量时享受折扣,如果购买量一旦超过了限量,则超出的部分将以原价销售。

class Limit_quote : public Quote
{
public:
    Limit_quote() = default;
    Limit_quote(const std::string& book, double price, std::size_t qty, double disc) : Quote(book, price), max_qty(qty), discount(disc) {}
    double net_price(std::size_t) const override;
private:
    std::size_t max_qty = 0;
    double discount;
};
double Limit_quote::net_price(size_t cnt) const
{
    if (cnt <= max_qty) {
        return cnt * (1-discount) * price;
    }
    else {
        return (cnt-max_qty) * price + max_qty * (1-discount) * price;
    }
}

15.8 给出静态类型和动态类型的定义。

表达式的静态类型在编译时总是已知的,它是变量声明时的类型或表达式生成的类型。

动态类型则是变量或表达式表示的内存中的对象的类型,动态类型直到运行时才可知。

15.9 在什么情况下表达式的静态类型可能与动态类型不同?请给出三个静态类型与动态类型不同的例子。

基类的指针或引用的静态类型可能与其动态类型不一致,如果表达式既不是指针也不是引用,则它的动态类型永远与静态类型一致。

例子:如15.03中的函数print_total中,参数item的静态类型是其定义的类型Quote&,但如果我们传递一个Bulk_quote&进去,则它的动态类型是Bulk_quote&,此例中item的静态类型和动态类型不一致。

15.10 回忆我们在8.1节(279页)进行的讨论,解释284页中将ifstream传递给Sales_data的read函数的程序是如何工作的。

read本是std::istream下的函数,但因为ifstream继承自istream,因此也可以使用read函数。

15.11 为你的Quote类体系添加一个名为debug的虚函数,令其分别显示每个类的数据成员。

// Quote类
virtual void debug() const;
void Quote::debug() const
{
    cout << "This is Quote class." << endl;
    cout << "bookNo = " << bookNo << " price = " << price << endl;
}
// Bulk_quote类
void debug() const override;
void Bulk_quote::debug() const
{
    cout << "This is Bulk_quote class." << endl;
    cout << " price = " << price << endl;
    cout << "min_qty = " << min_qty << " discount = " << discount << endl;
}
// Limit_quote类
void debug() const override;
void Limit_quote::debug() const
{
    cout << "This is Limit_quote class." << endl;
    cout << " price = " << price << endl;
    cout << "max_qty = " << max_qty << " discount = " << discount << endl;
}

15.12 有必要将一个成员函数同时声明成override和final吗?为什么?

有必要,override意味着重载父类中的虚函数,final意味着禁止子类重载该虚函数。两个用法并不冲突。

15.13 给定下面的类,解释每个print函数的机理:

class base {
public:
    string name() { return basename; }
    virtual void print (ostream& os) { os << basename; }
private:
	string basename = "home";    
};

class derived : public base {
public: 
    void print(ostream& os) { print(os); os << " " << i; }
    // 这里意为重写base类的print函数,并且在其中调用base的print(os),但是由于没有加::范围限定符,导致
    // 其调用的还是derived的print函数,造成无限递归。现象是打印完一行home之后就卡住了。
    // 改为:void print(ostream& os) override{ base::print(os); os << " " << i; }
    // 加override说明是覆盖基类的虚函数。
private:
    int i = 2;
};

在上述代码中存在问题吗?如果有,你该如何修改它?

15.14 给定上一题中的类以及下面这些对象,说明在运行时调用哪个函数:

base bobj;
base *bp1 = &bobj;
base &br1 = bobj;

derived dobj;
base *bp2 = &dobj;
base &br2 = dobj;
(a)bobj.print();	// base::print()
(b)dobj.print();	// derived::print()
(c)bp1->name();		// base::name()
(d)bp2->name();		// base::name()
(e)br1.print();		// base::print()
(f)br2.print();		// derived::print()

15.15 定义你自己的Disc_quote和Bulk_quote。

class Disc_quote : public Quote
{
public:
    Disc_quote() = default;
    Disc_quote(const std::string& book, double sales_price, std::size_t qty, double disc) : Quote(book, sales_price), quantity(qty), discount(disc){}
    virtual double net_price(std::size_t n) const override = 0;
protected:
    std::size_t quantity;
    double discount;
};

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节(第533页)练习中编写的数量受限的折扣策略,令其继承Disc_quote。

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 ); }
};

15.17 尝试定义一个Disc_quote的对象,看看编译器给出的错误信息是什么?

error: cannot declare variable 'dq' to be of abstract type 'Disc_quote'

15.18 假设给定了第543页和第544页的类,同时已知每个对象的类型如注释所示,判断下面的哪些赋值语句是合法的。解释那些不合法的语句为什么不被允许:

Base *p = &d1;			// d1的类型是Pub_Derv,合法
// 如果是保护或私有继承,则派生类不能向基类转换
p = &d2;				// d2的类型是Priv_Derv,非法
p = &d3;				// d3的类型是Prot_Derv,非法
p = &dd1;				// dd1的类型是Derived_from_Public,合法
p = &dd2;				// dd2的类型是Derived_from_Private,非法
p = &dd3;				// dd3的类型是Derived_from_Protected,非法

15.19 假设543页和544页的每个类都有如下形式的成员函数:

void memfcn(Base &b) { b = *this; }

对于每个类,分别判断上面的函数是否合法。

  1. 无论D以什么方式继承B,其成员函数和友元都能使用派生类到基类的转换。因此,Pub_Derv, Pro_Derv和Priv_Derv类中都合法。
  2. 如果D继承B的方式是共有的或受保护的,则D的派生类成员和友元可以使用D向B的类型转换,反之,如果D继承B是私有的,则不能使用,因此,Derived_from_Public合法,Derived_from_Private和Derived_from_Protected都不合法。

15.20 编写代码检验你对前面两题的回答是否正确。

// .h文件
class Base
{
public:
    void pub_mem();
protected:
    int prot_mem();
private:
    char priv_mem();
};

struct Pub_Derv : public Base
{
    int f() { return prot_mem(); }
	void memfcn(Base &b) { b = *this; }
};

struct Pro_Derv : protected Base
{
    int f() { return prot_mem(); }
	void memfcn(Base &b) { b = *this; }
};

struct Priv_Derv : private Base
{
    int f1() { return prot_mem(); }
	void memfcn(Base &b) { b = *this; }
};

struct Derived_from_Public : public Pub_Derv
{
    int use_base() {return prot_mem();}
    void memfcn(Base &b) { b = *this; }
};

struct Derived_from_Pro : public Pro_Derv
{
 //   void memfcn(Base &b) { b = *this; }
};

struct Derived_from_Private : public Priv_Derv
{
 //   void memfcn(Base &b) { b = *this; }
};

//.cpp文件
int main()
{
    Pub_Derv d1;
	Priv_Derv d2;
	Pro_Derv d3;
	Derived_from_Public dd1;
	Derived_from_Pro dd2;
	Derived_from_Private dd3;

    Base *p = &d1;			// d1的类型是Pub_Derv,合法
// 如果是保护或私有继承,则派生类不能向基类转换
//    p = &d2;				// d2的类型是Priv_Derv,非法
//    p = &d3;				// d3的类型是Prot_Derv,非法
    p = &dd1;				// dd1的类型是Derived_from_Public,合法
//    p = &dd2;				// dd2的类型是Derived_from_Private,非法
//    p = &dd3;				// dd3的类型是Derived_from_Protected,非法
	return 0;
}

15.21 从下面这些一般性抽象概念中任选一个(或者选一个你自己的),将其对应的一组类型组织成一个继承体系:

​ (a) 图形文件格式(如gif、tiff、jpeg、bmp)

​ (b) 图形基元(如方格、圆、球、圆锥)

​ (c) C++语言中的类型(如类、函数、成员函数)

15.22 对于你在上一题中选择的类,为其添加合适的虚函数及共有成员和受保护的成员。

class Shape
{
public:
    typedef std::pair<std::string, std::string> coordinate;
    Shape() = default;
    virtual double area() const = 0;
    virtual double perimeter() const = 0;   //ÇóÖܳ¤

    virtual ~Shape() = default;
//    virtual ~Shape() throw(){};
};

class Circle : public Shape
{
public:
    Circle(coordinate c, double r) : center(c), radius(r) {}
    double area() const override
    {
        return 3.14*radius*radius;
    }
    double perimeter() const override
    {
        return 2*3.14*radius;
    }
    ~ Circle() throw(){};
private:
    coordinate center;
protected:
    double radius;
};

class Circle_Cone : public Circle
{
public:
    double volume () const
    {
        return 3.14*radius*radius*height;
    }
private:
    double height;
};

​ 15.23 假设第520页的D1类需要覆盖它继承而来的fcn函数,你应该如何对其进行修改?如果你修改了之后fcn匹配了Base中的定义,则该节的那些调用语句应如何解析?

#include <iostream>
using namespace std;

class Base
{
public:
    virtual int fcn() { cout << "Base::fcn()" << endl; }
};

class D1 : public Base
{
public:
    int fcn(int);
    virtual int fcn() override{ cout << "d1::fcn()" << endl; }
    virtual void f2(){ cout << "d1::f2()" << endl; }
};

class D2 : public D1
{
public:
    int fcn(int);
    int fcn() override { cout << "d2::fcn()" << endl; }
    void f2() override { cout << "d2::f2()" << endl; }
};

int main()
{
    Base bobj;
    D1 d1obj;
    D2 d2obj;

    Base *bp1 = &bobj, *bp2 = &d1obj, *bp3 = &d2obj;

    bp1->fcn();		// 虚调用,运行时调用Base::fcn
    bp2->fcn();		// 虚调用,运行时调用D1::fcn
    bp3->fcn();		// 虚调用,运行时调用D2::fcn

    D1 *d1p = &d1obj; D2 *d2p = &d2obj;
//    bp2->f2();		// 错误,Base中没有f2成员。
    d1p->f2();		    // 虚调用,运行时调用D1::f2()
    d2p->f2();			// 虚调用,运行时调用D2::f2()
    return 0;
}

15.24 哪种类需要虚析构函数?虚析构函数必须执行什么样的操作?

作为基类,被其它类继承的类需要有虚析构函数,基类的析构函数定义为虚函数,可以允许子类中的对象动态销毁。

15.25 我们为什么为Disc_quote定义一个默认构造函数?如果去除掉该构造函数的话会对Bulk_quote的行为产生什么影响?

在我的编译器(g++ c++14)中,去除掉Disc_quote的默认构造函数,再定义Bulk_quote报错:

'Bulk_quote::Bulk_quote()' is implicitly deleted because the default definition would be ill-formed:

因为Disc_quote有自定义的构造函数,如果不显示声明,编译器不会再生成默认构造函数,这将阻止其子类生成默认构造函数,因此基类的默认构造函数应该显式声明,以便子类在执行默认构造函数的时候调用。

15.26 定义Quote和Bulk_quote的拷贝控制成员,令其与合成的版本行为一致。为这些成员以及其他构造函数添加打印状态的语句,使得我们能够知道正在运行哪个程序。使用这些类编写程序,预测程序将创建和销毁哪些对象。重复实验,不断比较你的预测和实际输出结果是否相同,直到预测完全准确再结束。

class Quote
{
public:
    Quote() = default;
    Quote(const std::string &book, double sales_price) : bookNo(book), price(sales_price) {}
    std::string isbn() const { return bookNo; }
    Quote (Quote& q) : bookNo(q.bookNo), price(q.price)
    {
        std::cout << "Quote copy construction." << std::endl;
    }

    Quote& operator=(Quote& rhs) noexcept
    {
        this->bookNo = rhs.bookNo;
        this->price = rhs.price;
        std::cout << "Quote assignment construction." << std::endl;
    }

    Quote(Quote&& q) noexcept : bookNo(std::move(q.bookNo)), price(std::move(q.price))
    {
        std::cout << "Quote move construction." << std::endl;
    }
    // return the total sales of given number books.
    // the derived class need overwrite the method of calculate the discount.
    virtual double net_price(std::size_t n) const { return n * price; }
    virtual ~Quote() = default;
private:
    std::string bookNo;     // isbn number
protected:
    double price = 0.0;     // origin price
};

class Disc_quote : public Quote
{
public:
    Disc_quote() = default;
    Disc_quote(const std::string& book, double sales_price, std::size_t qty, double disc) : Quote(book, sales_price), quantity(qty), discount(disc){}
    Disc_quote (Disc_quote& dq) : Quote(dq)
    {
        quantity = dq.quantity;
        discount = dq.discount;
        std::cout << "Disc_quote copy construction." << std::endl;
    }
    Disc_quote& operator=(Disc_quote& dq)
    {
        Quote::operator=(dq);
        quantity = dq.quantity;
        discount = dq.discount;
        std::cout << "Disc_quote assignment construction." << std::endl;
    }
    Disc_quote (Disc_quote&& dq) : Quote(dq), quantity(std::move(dq.quantity)), discount(std::move(dq.discount))
    {
        std::cout << "Disc_quote move construction." << std::endl;
    }
    virtual double net_price(std::size_t n) const override {}
protected:
    std::size_t quantity;
    double discount;
};

15.27 重新定义你的Bulk_quote类,令其继承构造函数。

class Bulk_quote : public Disc_quote
{
public:
    Bulk_quote()
    {
        cout << "Bulk_quote default construction." << endl;
    }

    using Disc_quote::Disc_quote;
    Bulk_quote(const std::string& b, double p, std::size_t q, double disc) :
        Disc_quote(b, p, q, disc){ }
    Bulk_quote (Bulk_quote& bq) : Disc_quote(bq)
    {
        cout << "Bulk_quote copy construction." << endl;
    }
    Bulk_quote& operator=(Bulk_quote& rhs)
    {
        Disc_quote::operator=(rhs);
        cout << "Bulk_quote assigned construction." << endl;
          return *this;
    }
    Bulk_quote(Bulk_quote&& bq) : Disc_quote(std::move(bq))
    {
        cout << "Bulk_quote move construction." << endl;
    }
    double net_price(std::size_t n) const override {}
};

15.28 定义一个存放Quote对象的vector,将Bulk_quote对象传入其中。计算vector中所有元素总的net_price。

int main()
{
    vector<Quote> v;

    v.push_back(Bulk_quote("ISO-1", 10, 10, 0.2));
    v.push_back(Bulk_quote("ISO-1", 10, 10, 0.2));
    v.push_back(Bulk_quote("ISO-1", 10, 10, 0.2));

    double total = 0;
    for (const auto& b : v)
    {
        total += b.net_price(20);
    }
    std::cout << total << std::endl;
    return 0;
}

15.29 再运行一次你的程序,这次传入Quote对象的shared_ptr。如果这次计算出的总额与之前的程序不一致,解释为什么;如果一致,也请说明原因。

int main()
{
    vector<shared_ptr<Quote>> pv;
    pv.push_back(make_shared<Bulk_quote>("ISO-1", 10, 10, 0.2));
    pv.push_back(make_shared<Bulk_quote>("ISO-1", 10, 10, 0.2));
    pv.push_back(make_shared<Bulk_quote>("ISO-1", 10, 10, 0.2));

    double total_p = 0;
    for (const auto& i : pv) {
        total_p += i->net_price(20);
    }
    cout << total_p << endl;
    return 0;
}

15.30 编写你自己的Basket类,用它计算上一个练习中交易记录的总价格。

#include <iostream>
#include <memory>
#include <set>
#include "Quote.h"

using namespace std;

class Basket
{
public:
    void add_item(const shared_ptr<Quote> &sales)
    {
        items.insert(sales);
    }
    double total_receipt (std::ostream&) const;     // 打印每本书的总价和购物篮中所有书的总价
private:
    static bool compare(const std::shared_ptr<Quote> &lhs, const std::shared_ptr<Quote> &rhs)
    {
        return lhs->isbn() < rhs->isbn();
    }
    // multiset保存多个报价,按照compare成员排序
    std::multiset<std::shared_ptr<Quote>, decltype(compare)*> items{compare};
};

double Basket::total_receipt(std::ostream &os) const
{
    double sum = 0.0;

    for (auto iter = items.cbegin(); iter != items.cend(); iter=items.upper_bound(*iter))
    {
        sum += print_total (os, **iter, items.count(*iter));
    }
    os << "Total Sale: " << sum << endl;
    return  sum;
}

// 测试时,引用Quote.h头文件,但其中声明的函数print_total在调用时报错“undefined reference to ***",应该是因为使用code blocks编辑器,链接的时候没有链接Quote.o文件导致的。将print_total函数放在Quote.h中实现,就不会报错了。

15.31 已知s1、s2、s3和s4都是string,判断下面的表达式分别创建了什么样的对象。

Query(s1) | Query(s2) & ~ Query(s3);
//  WordQuery, NotQuery, AndQuery, OrQuery, Query
Query(s1) | (Query(s2) & ~ Query(s3));
// WordQuery, NotQuery, AndQuery, OrQuery, Query
(Query(s1) | (Query(s2)) | (Query(s3) & Query(s4));
 // WordQuery, AndQuery, OrQuery, Query

15.32 当一个Query类型的对象被拷贝、移动、赋值或销毁时,将分别发生什么?

-- 拷贝:当Query对象被拷贝时,会调用合成的拷贝构造函数,拷贝Query的数据成员,成员q由于是shared_ptr,其引用计数会加1.

-- 移动:当Query对象被移动时,会调用合成移动构造函数,会移动数据成员到新的对象。在这个例子中,新对象中的shared_ptr会指向原对象的shared_ptr所指向的地址,新对象的shared_ptr的引用计数加1,原对象的shared_ptr的引用计数减1。

-- 赋值:调用合成的赋值函数,结果与拷贝操作相同。

-- 销毁:调用合成的析构函数,shared_ptr的引用计数减1,如果其引用计数减至0,则会释放它指向的内存。

15.33 当一个Query_base类型的对象被拷贝、移动、赋值或销毁时,将分别发生什么?

由于Query_base类中没有需要分配内存的数据成员,所以发生拷贝、移动、赋值或销毁,合成的版本就可以用,Query_base是抽象类,所以发生拷贝、移动、赋值或销毁时,操作的其实是它对应类型的子类。

15.34 针对图15.3构建的表达式:

(a) 列举出处理表达式的过程中执行的所有构造函数。

(b) 列举出cout << q所调用的rep。

(c) 列举出q.eval() 所调用的eval。

  • a. Query q = Query("firey") & Query("bird") | Query("wind");
    1. Query::Query(std::string& s) s分别是"firey", "bird", "wind"
    2. WordQuery::WordQuery(const std::string& s) s分别是"firey", "bird", "wind"
    3. AndQuery::AndQuery(const Query& left, const Query& right)
    4. BinaryQuery::BinaryQuery(const Query &l, const Query& r, std::string s)
    5. Query::Query (std::shared_ptr<Query_base> query) // when call q->eval, q->rep
    6. OrQuery::OrQuery(const Query& left, const Query& right)
    7. BinaryQuery::BinaryQuery(const Query &l, const Query& r, std::string s)
    8. Query::Query (std::shared_ptr<Query_base> query)
  • b. cout << q
    1. 在operator<<函数中调用的是Query的rep() ;
    2. Query中的rep调用OrQuery中继承Query_base的rep,因为生成对象q调用的是”|“运算返回的Query。但OrQuery中没有定义rep,因此调用的是Binary_Query中的rep;
    3. Binary_Query中的rep由lhs和rhs调用。lhs调用的是AndQuery中的rep,rhs调用的是Word_Query中的rep;
    4. AndQuery中调用的是Binary_Query中的rep;
    5. Binary_Query中的rep调用的是Word_Query中的rep。
  • c q.eval() 所调用的eval
    1. Query中的eval调用的是Query_base中的eval。
    2. 但这里Query_base指向的是OrQuery,所以调用的是OrQuery中的eval。

15.35 实现Query类和Query_base类,其中需要定义rep而无须定义eval。

#include "../ch12/textQuery.h"	// TextQuery和QueryResult类在12章已经定义过了,不再重复
#include "../ch12/textQuery.cpp"

#include <string>
// eval函数要加{},虚函数不能只声明,必须要有实现
class Query_base
{
    friend class Query;
protected:
    using line_no = TextQuery::line_no;     // for eval function
    virtual ~Query_base() = default;
private:
// eval return the QueryResult matches with the Query.
    virtual QueryResult eval (const TextQuery& ) const = 0;
// rep mean the query object is string.
    virtual std::string rep() const = 0;
};

class WordQuery : public Query_base
{
    friend class Query;
private:
    WordQuery(const std::string& s) : query_word(s)
    {
        std::cout << "WordQuery constructor. s = "  << s << std::endl;
    }
    QueryResult eval (const TextQuery& t) const
    {
        return t.query(query_word);
    }
    std::string rep() const
    {
        std::cout << "WordQuery::rep()" << std::endl;
        return query_word;
    }
    std::string query_word;
};

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 std::string& s) : q (new WordQuery(s))
    {
        std::cout << "Query constructor. s = "  << s << std::endl;
    }
    QueryResult eval(const TextQuery &t) const
    {
        return q->eval(t);
    }
    std::string rep () const
    {
        std::cout << "Query::rep()" << std::endl;
        return q->rep();
    }
private:
    Query (std::shared_ptr<Query_base> query) : q(query) {}
    std::shared_ptr<Query_base> q;
};

std::ostream& operator<< (std::ostream& os, Query& query)
{
    std::cout << "operator<<::rep()" << std::endl;
    return os << query.rep();
}

class NotQuery : public Query_base
{
    friend Query operator~(const Query&);
    NotQuery(const Query& q)  : query(q)
    {
        std::cout << "NotQuery constructor." << std::endl;
    }
    // virtual function inherit by Query_base.
    QueryResult eval (const TextQuery& ) const{}
    std::string rep() const override
    {
        std::cout << "NotQuery::rep()" << std::endl;
        return "~(" + query.rep() + ")";
    }
    Query query;
};

inline Query operator~(const Query& operand)
{
    return std::shared_ptr<Query_base> (new NotQuery(operand));
}

class BinaryQuery:public Query_base
{
protected:
    BinaryQuery(const Query &l, const Query& r, std::string s):
        lhs(l), rhs(r), opSym(s)
    {
        std::cout << "BinaryQuery constructor." << std::endl;
    }
    std::string rep() const
    {
        std::cout << "BinaryQuery::rep()" << std::endl;
        return "(" + lhs.rep() + " " + opSym + " " + rhs.rep() + ")";
    }
    Query lhs, rhs;
    std::string opSym; // operator name
};

class AndQuery : public BinaryQuery
{
    friend Query operator& (const Query&, const Query&);
    AndQuery(const Query& left, const Query& right) : BinaryQuery(left, right, "&")
    {
        std::cout << "AndQuery constructor." << std::endl;
    }
    QueryResult eval(const TextQuery& ) const{}
};

inline Query operator& (const Query& lhs, const Query& rhs)
{
    return std::shared_ptr<Query_base> (new AndQuery(lhs, rhs));
}

class OrQuery : public BinaryQuery
{
    friend Query operator| (const Query&, const Query&);
    OrQuery(const Query& left, const Query& right) : BinaryQuery(left, right, "|")
    {
        std::cout << "OrQuery constructor." << std::endl;
    }
    QueryResult eval(const TextQuery& ) const{}
};

inline Query operator| (const Query& lhs, const Query& rhs)
{
    return std::shared_ptr<Query_base> (new OrQuery(lhs, rhs));
}

15.36 在构造函数和rep成员中添加打印语句,运行你的代码以检验你对本节第一个练习中(a)、(b)两小题的回答是否正确。

#include "Query.h"
#include <iostream>

using namespace std;


int main()
{
    Query q = Query("firey") & Query("bird") | Query("wind");
    cout << q;
}
// 执行35、36中的程序,可以看到打印
Query q = Query("firey") & Query("bird") | Query("wind");的打印
WordQuery constructor. s = wind
Query constructor. s = wind
WordQuery constructor. s = bird
Query constructor. s = bird
WordQuery constructor. s = firey
Query constructor. s = firey
BinaryQuery constructor.
AndQuery constructor.
BinaryQuery constructor.
OrQuery constructor.

cout << q;的打印
operator<<::rep()
Query::rep()
BinaryQuery::rep()
Query::rep()
WordQuery::rep()
Query::rep()
BinaryQuery::rep()
Query::rep()
WordQuery::rep()
Query::rep()
WordQuery::rep(

15.37 如果在派生类中含有shared_ptr<Query_base>类型的成员而非Query类型的成员,则你的类需要做出怎样的改变。

不需要做改变。

15.38 下面的声明合法吗?如果不合法,请解释原因:如果合法,请指出该声明的含有。

BinaryQuery a = Query("firey") & Query("bird");
// 不合法,因为BinaryQuery是一个抽象类
AndQuery b = Query("firry") & Query("bird");
// 不合法,因为&操作返回的是Query操作,不能转换为AndQuery
OrQuery c = Query("firey") & Query("bird");
// 不合法,同上。

15.39 实现Query类和Query_base类,求图15.3中表达式的值并打印相关信息,验证你的程序是否正确。

// Query.cpp的实现。 主要要使用strBlob版本的textQuery

#include "Query.h"
#include <iostream>
#include <algorithm>

using namespace std;

QueryResult OrQuery::eval(const TextQuery& text) const
{
    // call Query::eval() --> Query_base::eval() --> QueryResult::eval()
    QueryResult right = rhs.eval(text), left = lhs.eval(text);
    auto ret_line = make_shared<set<line_no>>(left.begin(), left.end());
    ret_line->insert(right.begin(), right.end());
    return QueryResult(rep(), ret_line, left.get_file());
}

QueryResult AndQuery::eval(const TextQuery& text) const
{
    QueryResult right = rhs.eval(text), left = lhs.eval(text);
 //   auto ret_line = make_shared<set<line_no>> (left.begin(), left.end());
    shared_ptr<std::set<line_no>> ret_lines =
            std::make_shared<std::set<line_no>>(left.begin(), left.end());
    set_intersection(left.begin(), left.end(), right.begin(), right.end(), inserter(*ret_lines, ret_lines->begin()));
    return QueryResult(rep(), ret_lines, left.get_file());
}

QueryResult NotQuery::eval(const TextQuery& text) const
{
    auto result = query.eval(text);
    auto ret_lines = make_shared<set<line_no>>();
    auto beg = result.begin(), end = result.end();
    auto sz = result.get_file().size();
    for (size_t i = 0; i < sz; ++ i)
    {
        if (beg == end || *beg != i)
        {
            ret_lines->insert(i);
        }
        else if (beg != end)
        {
            ++ beg;
        }
    }
    return QueryResult(rep(), ret_lines, result.get_file());
}
int main()
{
    Query q = Query("firey") & Query("bird") | Query("wind");
    cout << q;
}

// 打印的结果 ((firey & bird) | wind)

15.40 在OrQuery的eval函数中,如果rhs成员返回的是空集将发生什么?如果lhs是空集呢?如果lhs和rhs都是空集又将发生什么?

不会发生什么。

auto ret_line = make_shared<set<line_no>>(left.begin(), left.end());

这行代码,make_shared 将会动态分配set,如果返回的是空集,set中就不会插入值。

15.41 重新实现你的类,这次使用指向Query_base的内置指针而非shared_ptr。请注意,做出上述改动后你的类不能再使用合成的拷贝控制成员。

15.42 从下面的几种改进中选择一种,设计并实现它:

(c)允许用户对结果做出限制,比如从给定范围的行中挑出匹配的进行显示。

posted @ 2018-12-18 22:38  安月月  阅读(2904)  评论(0编辑  收藏  举报