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)允許用戶對結果做出限制,比如從給定范圍的行中挑出匹配的進行顯示。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM