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