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; }
對於每個類,分別判斷上面的函數是否合法。
- 無論D以什么方式繼承B,其成員函數和友元都能使用派生類到基類的轉換。因此,Pub_Derv, Pro_Derv和Priv_Derv類中都合法。
- 如果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");
Query::Query(std::string& s)
s分別是"firey", "bird", "wind"WordQuery::WordQuery(const std::string& s)
s分別是"firey", "bird", "wind"AndQuery::AndQuery(const Query& left, const Query& right)
BinaryQuery::BinaryQuery(const Query &l, const Query& r, std::string s)
Query::Query (std::shared_ptr<Query_base> query)
// when call q->eval, q->repOrQuery::OrQuery(const Query& left, const Query& right)
BinaryQuery::BinaryQuery(const Query &l, const Query& r, std::string s)
Query::Query (std::shared_ptr<Query_base> query)
- b. cout << q
- 在operator<<函數中調用的是Query的rep() ;
- Query中的rep調用OrQuery中繼承Query_base的rep,因為生成對象q調用的是”|“運算返回的Query。但OrQuery中沒有定義rep,因此調用的是Binary_Query中的rep;
- Binary_Query中的rep由lhs和rhs調用。lhs調用的是AndQuery中的rep,rhs調用的是Word_Query中的rep;
- AndQuery中調用的是Binary_Query中的rep;
- Binary_Query中的rep調用的是Word_Query中的rep。
- c q.eval() 所調用的eval
- Query中的eval調用的是Query_base中的eval。
- 但這里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)允許用戶對結果做出限制,比如從給定范圍的行中挑出匹配的進行顯示。