zoukankan      html  css  js  c++  java
  • 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)允许用户对结果做出限制,比如从给定范围的行中挑出匹配的进行显示。

  • 相关阅读:
    【leetcode】704.BinarySearch
    【leetcode】75.Sort Colors
    MongoChef
    问题 工具的缺陷
    MongoDB
    SpringFox
    npm 包管理工具
    RESTful 设计工具和Web框架
    笔记_JSON
    jsoup: Java HTML Parser
  • 原文地址:https://www.cnblogs.com/songshuguiyu/p/10140619.html
Copyright © 2011-2022 走看看