练习 15.1:
基类中的虚成员希望它的派生类各自定义适合自身的版本。基类通常都应该定义虚析构函数,即使该函数不执行任何实际操作。
练习 15.2:
private: 基类本身和友元可以访问。
protected: 基类本身、友元和派生类都可以访问。
练习 15.3:
#include<iostream>
#include<string>
using namespace std;
class Quote
{
public:
Quote() = default;
Quote(const string& book, double sales_price):
bookNo(book), price(sales_price){}
string isbn()const { return bookNo; }
virtual double net_price(size_t n)const { return n * price; }
virtual ~Quote() = default;
private:
string bookNo;
protected:
double price = 0.0;
};
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.4:
class Base { ... };
(a) class Derived : public Derived { ... }; // 不正确,一个类不能派生它本身
(b) class Derived : private Base { ... }; // 正确,Derived从他的基类Base派生,且规定派生类从基类继承的数据成员对于派生类的用户是不可见。
(c) class Derived : public Base; // 不正确,派生类的声明与其他类相差不大,声明中包含类名但是不包含他的派生列表;
练习 15.5:
#include"Quote.h"
class Bulk_quote :public Quote
{
public:
Bulk_quote() = default;
Bulk_quote(const string& book, double p, size_t qty, double disc):
Quote(book, p), min_qty(qty), discount(disc){}
double net_price(size_t cnt)const override
{
if (cnt >= min_qty)
return cnt * (1 - discount) * price;
else
return cnt * price;
}
protected:
size_t min_qty = 0; // 适用折扣政策的最低购买量
double discount = 0.0; // 以小数表示的折扣额
};
练习 15.6:
#include"Quote.h"
#include"Bulk_quote.h"
int main()
{
Quote quote("0-201-78345-X", 23.8);
print_total(cout, quote, 3);
Bulk_quote bulk_quote("0-201-78345-X", 23.8, 3, 0.1);
print_total(cout, bulk_quote, 4);
}
练习 15.7:
#include"Quote.h"
class Bulk_quote :public Quote
{
public:
Bulk_quote() = default;
Bulk_quote(const string& book, double p, size_t qty, double disc):
Quote(book, p), min_qty(qty), discount(disc){}
double net_price(size_t cnt)const override
{
if (cnt >= min_qty)
return cnt * (1 - discount) * price;
else
return cnt * price;
}
protected:
size_t min_qty = 0; // 适用折扣政策的最低购买量
double discount = 0.0; // 以小数表示的折扣额
};
练习 15.8:
- 静态类型: 表达式的静态类型在编译时总是已知的,它是变量声明时或表达式生成的类型。
- 动态类型: 动态类型是变量或表达式表示的内存中的对象的类型。动态类型直到运行时才可知。
练习 15.9:
基类的指针或引用指向派生类对象的时候。在这种情况下,静态类型是基类的指针或引用,而动态类型是派生类的指针或引用。
double ret = item.net_price(n);
例如,当print_total 调用 net_price 时,item的静态类型是Quote&,它的动态类型依赖于item绑定的实参,如果传递的对象是Bulk_quote,则此例中动态类型为Bulk_quote。
练习 15.10:
该函数接受一个istream&参数,当传递一个派生类ifstream对象时,执行从派生类到基类的转换,这就是read函数工作的原因。
练习 15.11:
#ifndef QUOTE_H
#define QUOTE_H
#include<iostream>
#include<string>
using namespace std;
class Quote
{
public:
Quote() = default;
Quote(const string& book, double sales_price) :
bookNo(book), price(sales_price) {}
string isbn()const { return bookNo; }
virtual double net_price(size_t n)const { return n * price; }
virtual void debug()const{ cout << "data members: \n"
<< "\tbookNo: " << bookNo << "\tprice: " << price << endl; }
virtual ~Quote() = default;
private:
string bookNo;
protected:
double price = 0.0;
};
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;
}
#endif
#ifndef BULK_QUOTE_H
#define BULK_QUOTE_H
#include"Quote.h"
class Bulk_quote :public Quote
{
public:
Bulk_quote() = default;
Bulk_quote(const string& book, double p, size_t qty, double disc):
Quote(book, p), min_qty(qty), discount(disc){}
virtual double net_price(size_t cnt)const override
{
if (cnt >= min_qty)
return cnt * (1 - discount) * price;
else
return cnt * price;
}
virtual void debug()const override
{
Quote::debug();
cout << "\tmin_qty: " << min_qty << "\tdiscount: " << discount << endl;
}
protected:
size_t min_qty = 0; // 适用折扣政策的最低购买量
double discount = 0.0; // 以小数表示的折扣额
};
#endif
#ifndef LIMIT_QUOTE_H
#define LIMIT_QUOTE_H
#include"Bulk_quote.h"
class Limit_quote : public Bulk_quote
{
public:
Limit_quote() = default;
Limit_quote(const string& book, double p, size_t min, size_t max, double disc):
Bulk_quote(book, p, min, disc), max_qty(max){}
virtual double net_price(size_t cnt)const override
{
if (cnt > max_qty)
return max_qty * (1 - discount) * price + (cnt - max_qty) * price;
else if (cnt >= min_qty)
return cnt * (1 - discount) * price;
else
return cnt * price;
}
virtual void debug()const override
{
Bulk_quote::debug();
cout << "\tmax_qty " << max_qty << endl;
}
private:
size_t max_qty = 0; // 适用折扣政策的最高购买量
};
#endif
练习 15.12:
有必要。override:使用override关键字来说明派生类中的虚函数,这么做的好处是在使得我们的意图更加清晰明确地告诉编译器我们想要覆盖掉已存在的虚函数。如果定义了一个虚函数与基类中的名字相同但是形参列表不同,在不使用override关键字的时候这种函数定义是合法的,在使用了override关键字之后这种行为是非法的,编译器会提示出错。final:如果我们将某个函数定义成final,则不允许后续的派生类来覆盖这个函数,否则会报错。因此同时将一个成员函数声明成override和final能够使我们的意图更加清晰。
练习 15.13:
class base {
public:
string name() { return basename; }
virtual void print(ostream &os) { os << basename; } // 输出base数据成员
private:
string basename;
};
class derived : public base {
public:
void print(ostream &os) { print(os); os << " " << i; } // 输出derived数据成员; 想要从基类调用print,缺少类作用域base::
private:
int i;
};
derived类中的print想要从base类调用print。但是,类作用域base::被省略了。因此,它将导致无限递归。修改:
void print(ostream &os) { base::print(os); os << " " << i; }
练习 15.14:
base bobj; base *bp1 = &bobj; base &br1 = bobj;
derived dobj; base *bp2 = &dobj; base &br2 = dobj;
bobj.print(); // base::print()
dobj.print(); // derived::print()
bp1->name(); // base::name()
bp2->name(); // base::name()
br1.print(); // base::print()
br2.print(); // derived::print()
练习 15.15:
#ifndef DISC_QUOTE_H
#define DISC_QUOTE_H
#include"Quote.h"
class Disc_quote :public Quote
{
public:
Disc_quote() = default;
Disc_quote(const string& book, double p, size_t qty, double disc) :
Quote(book, p), quantity(qty), discount(disc) {}
virtual double net_price(size_t cnt)const = 0;
virtual void debug()const override
{
Quote::debug();
cout << "\tquantity: " << quantity << "\tdiscount: " << discount << endl;
}
protected:
size_t quantity = 0; // 折扣适用的购买量
double discount = 0.0; // 表示折扣的小数值
};
#endif
#ifndef BULK_QUOTE_15_H
#define BULK_QUOTE_15_H
#include"Disc_quote.h"
class Bulk_quote :public Disc_quote
{
public:
Bulk_quote() = default;
Bulk_quote(const string& book, double p, size_t qty, double disc) :
Disc_quote(book, p, qty, disc) {}
virtual double net_price(size_t cnt)const override
{
if (cnt >= quantity)
return cnt * (1 - discount) * price;
else
return cnt * price;
}
};
#endif
练习 15.16:
#ifndef LIMIT_QUOTE_16_H
#define LIMIT_QUOTE_16_H
#include"Disc_quote.h"
class Limit_quote : public Disc_quote
{
public:
Limit_quote() = default;
Limit_quote(const string& book, double p, size_t min, size_t max, double disc) :
Disc_quote(book, p, min, disc), max_qty(max) {}
virtual double net_price(size_t cnt)const override
{
if (cnt > max_qty)
return max_qty * (1 - discount) * price + (cnt - max_qty) * price;
else if (cnt >= quantity)
return cnt * (1 - discount) * price;
else
return cnt * price;
}
virtual void debug()const override
{
Disc_quote::debug();
cout << "\tmax_qty: " << max_qty << endl;
}
private:
size_t max_qty = 0; // 适用折扣政策的最高购买量
};
#endif
练习 15.17:
不允许使用抽象类类型"Disc_quote"的对象: 函数"Disc_quote::net_price"是纯虚函数。
练习 15.18:
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 —— 不合法
只有当D公有地继承B时,用户代码才能使用派生类到基类的转换;如果D继承B的方式是受保护的或私有的,则用户代码不能使用该转换。
练习 15.19:
class Base
{
public:
void pub_mem();
void memfcn(Base& b) { b = *this; } // 合法
protected:
int prot_mem;
private:
char priv_mem;
};
struct Pub_Derv : public Base
{
int f() { return this->prot_mem; }
void memfcn(Base& b) { b = *this; } // 合法
};
struct Prot_Derv : protected Base
{
int f1() { return this->prot_mem; }
void memfcn(Base& b) { b = *this; } // 合法
};
struct Priv_Derv : private Base
{
int f1() { return this->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_Protected : public Prot_Derv
{
int use_base() { return prot_mem; }
void memfcn(Base& b) { b = *this; } // 合法
};
struct Derived_from_Private : public Priv_Derv
{
void memfcn(Base& b) { b = *this; } // 不合法
};
如果D继承B的方式是共有的或者受保护的,则D的派生类的成员和友元可以使用派生类到基类的转换。如果D继承B的方式是私有的,则不能使用。
练习 15.20:
class Base
{
public:
void pub_mem();
void memfcn(Base& b) { b = *this; } // 合法
protected:
int prot_mem;
private:
char priv_mem;
};
struct Pub_Derv : public Base
{
int f() { return this->prot_mem; }
void memfcn(Base& b) { b = *this; } // 合法
};
struct Prot_Derv : protected Base
{
int f1() { return this->prot_mem; }
void memfcn(Base& b) { b = *this; } // 合法
};
struct Priv_Derv : private Base
{
int f1() { return this->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_Protected : public Prot_Derv
{
int use_base() { return prot_mem; }
void memfcn(Base& b) { b = *this; } // 合法
};
struct Derived_from_Private : public Priv_Derv
{
void memfcn(Base& b) { b = *this; } // 不合法
};
int main()
{
Pub_Derv d1;
Priv_Derv d2;
Prot_Derv d3;
Derived_from_Public dd1;
Derived_from_Private dd2;
Derived_from_Protected dd3;
Base* p = &d1;
p = &d2;
p = &d3;
p = &dd1;
p = &dd2;
p = &dd3;
}
练习 15.21 && 15.22:
#ifndef Graphics_primitives_H
#define Graphics_primitives_H
static const float PI = 3.14159f;
class Shape
{
public:
virtual const char* shape_name() = 0;
virtual void resize_by_percentage(float pct) = 0;
virtual ~Shape() {}
};
class Shape_2D : public Shape
{
public:
Shape_2D() = default;
Shape_2D(float cx, float cy) : center_x(cx), center_y(cy) {}
virtual float diamer()const = 0;
virtual float area()const = 0;
virtual float circumferenceconst()const = 0;
virtual ~Shape_2D()override {}
protected:
float center_x = 0.f;
float center_y = 0.f;
};
class Shape_3D : public Shape
{
public:
Shape_3D() = default;
Shape_3D(float cx, float cy, float cz) : center_x(cx), center_y(cy), center_z(cz) {}
virtual float volume()const = 0;
virtual ~Shape_3D()override {}
protected:
float center_x = 0.f;
float center_y = 0.f;
float center_z = 0.f;
};
class Box : public Shape_3D
{
public:
Box() = default;
explicit Box(float x, float y, float z) : size_x(x), size_y(y), size_z(z) {}
Box(float cx, float cy, float cz, float x, float y, float z)
: Shape_3D(cx, cy, cz), size_x(x), size_y(y), size_z(z) {}
virtual float volume()const override { return size_x * size_y * size_z; }
virtual const char* shape_name()override { return "Box"; }
virtual void resize_by_percentage(float pct)override
{
size_x *= pct;
size_y *= pct;
size_z *= pct;
}
virtual ~Box() {}
protected:
float size_x = 1.0f;
float size_y = 1.0f;
float size_z = 1.0f;
};
class Circle : public Shape_2D
{
public:
Circle() = default;
explicit Circle(float r) : radius(r) {}
Circle(float cx, float cy, float r) : Shape_2D(cx, cy), radius(r) {}
virtual float diamer()const override { return 2 * radius; }
virtual float area()const override { return PI * radius * radius; }
virtual float circumferenceconst()const override { return 2 * PI * radius; }
virtual const char* shape_name()override { return "Circle"; }
virtual void resize_by_percentage(float pct)override { radius *= pct; }
virtual ~Circle() {}
protected:
float radius = 1.f;
};
class Sphere : public Shape_3D
{
public:
Sphere() = default;
explicit Sphere(float r) : radius(r) {}
Sphere(float cx, float cy, float cz, float r) : Shape_3D(cx, cy, cz), radius(r) {}
virtual float volume()const override { return 4 * PI * radius * radius * radius / 3; }
virtual const char* shape_name()override { return "Sphere"; }
virtual void resize_by_percentage(float pct)override { radius *= pct; }
virtual ~Sphere() {}
protected:
float radius = 1.f;
};
class Cone : public Shape_3D
{
public:
Cone() = default;
explicit Cone(float r, float h) : radius(r), height(h) {}
Cone(float cx, float cy, float cz, float r, float h) : Shape_3D(cx, cy, cz), radius(r), height(h) {}
virtual float volume()const override { return PI * radius * radius * radius / 3; }
virtual const char* shape_name()override { return "Cone"; }
virtual void resize_by_percentage(float pct)override
{
radius *= pct;
height *= pct;
}
virtual ~Cone() {}
protected:
float radius = 1.f;
float height = 1.f;
};
#endif
练习 15.23:
#include<iostream>
using namespace std;
class Base
{
public:
virtual int fcn() { cout << "Base::fcn()" << endl; return 0; }
};
class D1 : public Base
{
public:
int fcn()override { cout << "D1::fcn()" << endl; return 0; }
int fcn(int) { cout << "D1::fcn(int)" << endl; return 0; }
virtual void f2(){ cout << "D1::f2()" << endl; }
};
class D2 : public D1
{
public:
int fcn(int) { cout << "D2::fcn(int)" << endl; return 0; }
int fcn()override { cout << "D2::fcn()" << endl; return 0; }
void f2()override { cout << "D2::f2()" << endl; }
};
int main()
{
Base bobj; D1 d1obj; D2 d2obj;
Base* bp1 = &bobj; Base* bp2 = &d1obj; Base* bp3 = &d2obj;
bp1->fcn(); // Base::fcn()
bp2->fcn(); // D1::fcn()
bp3->fcn(); // D2::fcn()
D1* d1p = &d1obj; D2* d2p = &d2obj;
//bp2->f2();
d1p->f2(); // D1::f2()
d2p->f2(); // D2::f2()
Base* p1 = &d2obj; D1* p2 = &d2obj; D2* p3 = &d2obj;
//p1->fcn(42);
p2->fcn(42); // D1::fcn(int)
p3->fcn(42); // D2::fcn(int)
return 0;
}
练习 15.24:
基类通常应该定义一个虚析构函数,这样我们就能动态分配继承体系中的对象了。
练习 15.25:
#include <string>
using namespace std;
class Quote
{
public:
Quote() = default;
Quote(const string& book, double sales_price) :
bookNo(book), price(sales_price) {}
string isbn()const { return bookNo; }
virtual double net_price(size_t n)const { return n * price; }
virtual ~Quote() = default;
private:
string bookNo;
protected:
double price = 0.0;
};
class Disc_quote :public Quote
{
public:
//Disc_quote() = default;
Disc_quote(const string& book, double p, size_t qty, double disc) :
Quote(book, p), quantity(qty), discount(disc) {}
virtual double net_price(size_t cnt)const = 0;
protected:
size_t quantity = 0;
double discount = 0.0;
};
class Bulk_quote :public Disc_quote
{
public:
Bulk_quote() = default;
Bulk_quote(const string& book, double p, size_t qty, double disc) :
Disc_quote(book, p, qty, disc) {}
virtual double net_price(size_t cnt)const override
{
if (cnt >= quantity)
return cnt * (1 - discount) * price;
else
return cnt * price;
}
};
int main()
{
Bulk_quote b_quote; // 无法使用Bulk_quote的默认构造函数 —— 它是已删除的函数
}
原因是Disc_quote已经定义了一个带有4个参数的构造函数,这阻止了编译器生成合成版本的默认构造函数。因此,从它派生的任何类的默认构造函数都被定义为删除,即使派生类中定义了默认构造函数。因此,必须显式定义默认构造函数,以便派生类在执行其默认构造函数时可以调用它。
练习 15.26:
#ifndef QUOTE_26_H
#define QUOTE_26_H
#include<iostream>
#include<string>
using namespace std;
class Quote
{
public:
Quote() { cout << "Quote Constructor" << endl; }
Quote(const string& book, double sales_price) :
bookNo(book), price(sales_price) {
cout << "Quote Constructor with two parameters" << endl;
}
Quote(const Quote& rhs) : bookNo(rhs.bookNo), price(rhs.price) {
cout << "Quote Copy Constructor" << endl;
}
Quote& operator=(const Quote& rhs) {
cout << "Quote Copy assignment operator" << endl;
bookNo = rhs.bookNo;
price = rhs.price;
return *this;
}
Quote(Quote&& rhs)noexcept :
bookNo(std::move(rhs.bookNo)), price(std::move(rhs.price)) {
cout << "Quote Move Constructor" << endl;
}
Quote& operator=(Quote&& rhs)noexcept {
cout << "Quote Move assignment operator" << endl;
bookNo = std::move(rhs.bookNo);
price = std::move(rhs.price);
return *this;
}
string isbn()const { return bookNo; }
virtual double net_price(size_t n)const { return n * price; }
virtual void debug()const {
cout << "data members: \n"
<< "\tbookNo: " << bookNo << "\tprice: " << price << endl;
}
virtual ~Quote() { cout << "Quote Destructor" << endl; }
private:
string bookNo;
protected:
double price = 0.0;
};
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;
}
#endif
#ifndef BULK_QUOTE_26_H
#define BULK_QUOTE_26_H
#include"Quote_26.h"
class Bulk_quote :public Quote
{
public:
Bulk_quote() { cout << "Bulk_quote Constructor" << endl; }
Bulk_quote(const string& book, double p, size_t qty, double disc) :
Quote(book, p), min_qty(qty), discount(disc) {
cout << "Bulk_quote Constructor with four parameters" << endl;
}
Bulk_quote(const Bulk_quote& rhs):
Quote(rhs), min_qty(rhs.min_qty), discount(rhs.discount){
cout << "Bulk_quote Copy Constructor" << endl; }
Bulk_quote& operator=(const Bulk_quote& rhs){
cout << "Bulk_quote Copy assignment operator" << endl;
Quote::operator=(rhs);
min_qty = rhs.min_qty;
discount = rhs.discount;
return *this;
}
Bulk_quote(Bulk_quote&& rhs)noexcept :
Quote(std::move(rhs)), min_qty(std::move(rhs.min_qty)), discount(std::move(rhs.discount)) {
cout << "Bulk_quote Move Constructor" << endl;
}
Bulk_quote& operator=(Bulk_quote&& rhs)noexcept {
cout << "Bulk_quote Move assignment operator" << endl;
Quote::operator=(std::move(rhs));
min_qty = std::move(rhs.min_qty);
discount = std::move(rhs.discount);
return *this;
}
virtual double net_price(size_t cnt)const override
{
if (cnt >= min_qty)
return cnt * (1 - discount) * price;
else
return cnt * price;
}
virtual void debug()const override
{
Quote::debug();
cout << "\tmin_qty: " << min_qty << "\tdiscount: " << discount << endl;
}
virtual ~Bulk_quote(){ cout << "Bulk_quote Destructor" << endl; }
protected:
size_t min_qty = 0;
double discount = 0.0;
};
#endif
练习 15.28:
#include"Bulk_quote.h"
#include<vector>
#include<numeric>
int main()
{
vector<Quote> VecQuote;
Bulk_quote bulk1("0-201-82470-1", 50, 10, 0.2);
Bulk_quote bulk2("0-201-82470-1", 50, 8, 0.1);
cout << "Bulk_quote's total net price: "
<< bulk1.net_price(10) + bulk2.net_price(10) << endl; // 850
VecQuote.push_back(bulk1);
VecQuote.push_back(bulk2);
auto total = accumulate(VecQuote.cbegin(), VecQuote.cend(), 0.0,
[](double ret, const Quote& q) {return ret += q.net_price(10); });
cout << "Bulk_quote's total net price in vector: " << total << endl; // 1000
return 0;
}
练习 15.29:
#include"Bulk_quote.h"
#include<vector>
#include<numeric>
int main()
{
vector<shared_ptr<Quote>> VecQuotePtr;
auto pBulk1 = make_shared<Bulk_quote>("0-201-82470-1", 50, 10, 0.2);
auto pBulk2 = make_shared<Bulk_quote>("0-201-82470-1", 50, 8, 0.1);
cout << "Bulk_quote's total net price: "
<< pBulk1->net_price(10) + pBulk2->net_price(10) << endl; // 850
VecQuotePtr.push_back(pBulk1);
VecQuotePtr.push_back(pBulk2);
auto total = accumulate(VecQuotePtr.cbegin(), VecQuotePtr.cend(), 0.0,
[](double ret, shared_ptr<Quote> p) {return ret += p->net_price(10); });
cout << "Bulk_quote's total net price in vector: " << total << endl; // 850
return 0;
}
练习 15.30:
#ifndef QUOTE_30_H
#define QUOTE_30_H
#include<iostream>
#include<string>
using namespace std;
class Quote
{
public:
Quote() = default;
Quote(const string& book, double sales_price) :
bookNo(book), price(sales_price) {}
virtual Quote* clone()const& { return new Quote(*this); }
virtual Quote* clone()&& { return new Quote(std::move(*this)); }
string isbn()const { return bookNo; }
virtual double net_price(size_t n)const { return n * price; }
virtual void debug()const {
cout << "data members: \n"
<< "\tbookNo: " << bookNo << "\tprice: " << price << endl;
}
virtual ~Quote() = default;
private:
string bookNo;
protected:
double price = 0.0;
};
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;
}
#endif
#ifndef BULK_QUOTE_30_H
#define BULK_QUOTE_30_H
#include"Quote_30.h"
class Bulk_quote :public Quote
{
public:
Bulk_quote() = default;
Bulk_quote(const string& book, double p, size_t qty, double disc) :
Quote(book, p), min_qty(qty), discount(disc) {}
virtual Bulk_quote* clone()const& { return new Bulk_quote(*this); }
virtual Bulk_quote* clone()&& { return new Bulk_quote(std::move(*this)); }
virtual double net_price(size_t cnt)const override
{
if (cnt >= min_qty)
return cnt * (1 - discount) * price;
else
return cnt * price;
}
virtual void debug()const override
{
Quote::debug();
cout << "\tmin_qty: " << min_qty << "\tdiscount: " << discount << endl;
}
protected:
size_t min_qty = 0; // 适用折扣政策的最低购买量
double discount = 0.0; // 以小数表示的折扣额
};
#endif
#include"Bulk_quote_30.h"
#include<set>
using namespace std;
class Basket
{
public:
void add_item(const Quote& sale)
{ items.insert(shared_ptr<Quote>(sale.clone())); }
void add_item(Quote&& sale)
{ items.insert(shared_ptr<Quote>(std::move(sale).clone())); }
// 打印每本书的总价和购物篮中所有书的总价
double total_receipt(ostream&)const;
private:
static bool compare(const shared_ptr<Quote>& lhs, const shared_ptr<Quote>& rhs)
{ return lhs->isbn() < rhs->isbn(); }
multiset<shared_ptr<Quote>, decltype(compare)*> items{compare};
};
double Basket::total_receipt(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;
}
int main()
{
Basket bsk;
for (size_t i = 0; i != 10; ++i)
{
bsk.add_item(Quote("Cpp", 40));
bsk.add_item(Bulk_quote("Java", 50, 10, 0.2));
bsk.add_item(Bulk_quote("Python", 50, 8, 0.1));
}
bsk.total_receipt(cout);
return 0;
}
练习 15.31:
逻辑与的优先级高于逻辑或
- (a): WordQuery -> NotQuery -> AndQuery -> OrQuery
- (b): WordQuery -> NotQuery -> AndQuery -> OrQuery
- (c): AndQuery -> AndQuery -> OrQuery
练习 15.32:
- 拷贝: 发生拷贝时,调用合成拷贝构造函数。它将数据成员拷贝到新对象中。由于在这种情况下,数据成员是一个shared_ptr,在拷贝时,相应的shared_ptr指向相同的地址,引用计数加一。
- 移动: 被移动时,调用合成的移动构造函数。它将数据成员移动到新对象中。在这种情况下,来自新创建对象的shared_ptr将指向原始shared_ptr所指向的地址。在move操作之后,shared_ptr在新对象中的引用计数为1,而来自原始对象的指针变为nullptr。
- 拷贝赋值: 合成拷贝赋值运算符将被调用。此操作的结果与拷贝操作相同。
- 移动赋值: 调用合成的移动赋值运算符。此操作的结果与移动操作相同。
- 析构: 调用合成析构函数。它将调用shared_ptr的析构函数,该函数将减少shared_ptr的引用计数。如果引用计数变为零,shared_ptr的析构函数将删除它所指向的资源。
练习 15.33:
由于Query_base是一个抽象类,Query_base没有定义自己的拷贝/移动控制成员,实际上它没有任何数据成员,无须定义这些操作,因此进行这些操作时,执行默认语句,什么也不会发生。
练习 15.34:
(a): Query q = Query("fiery") & Query("bird") | Query("wind");
- WordQuery::WordQuery(wind)
- Query::Query(const string& s) where s=wind
- WordQuery::WordQuery(bird)
- Query::Query(const string& s) where s=bird
- WordQuery::WordQuery(fiery)
- Query::Query(const string& s) where s=fiery
- BinaryQuery::BinaryQuery() where s=&
- AndQuery::AndQuery()
- Query::Query(shared_ptr<Query_base> query)
- BinaryQuery::BinaryQuery() where s=|
- OrQuery::OrQuery
- Query::Query(shared_ptr<Query_base> query)
(b): cout << q 所调用的rep
- Query::rep() 操作符<<的调用
- BinaryQuery::rep() OrQuery::rep()继承BinaryQuery
- Query::rep() OrQuery 的右侧
- WodQuery::rep() Query("wind")
- Query::rep() OrQuery 的左侧
- BinaryQuery::rep() AndQuery::rep()继承BinaryQuery
- Query::rep() AndQuery 的右侧
- WodQuery::rep() Query("bird")
- Query::rep() AndQuery 的左侧
- WodQuery::rep() Query("fiery")
输出:((fiery&bird)|wind)
(c): q.eval()所调用的eval
- Query::eval()
- q->eval()
- OrQuery::eval()
练习 15.35:
#include"TextQuery.h"
class Query_base
{
friend class Query;
protected:
using line_no = TextQuery::line_no;
virtual ~Query_base() = default;
private:
// eval返回与当前Query匹配的QueryResult
virtual QueryResult eval(const TextQuery&)const = 0;
// rep是表示查询的一个string
virtual string rep()const = 0;
};
class Query
{
friend Query operator~(const Query&);
friend Query operator|(const Query&, const Query&);
friend Query operator&(const Query&, const Query&);
public:
Query(const string&); // 构建一个新的WordQuery
// 接口函数:调用对应的Query_Base操作
QueryResult eval(const TextQuery& t)const { return q->eval(t); }
string rep()const { return q->rep(); }
private:
Query(shared_ptr<Query_base> query) : q(query) {}
shared_ptr<Query_base> q;
};
ostream& operator<<(ostream& os, const Query& query)
{
return os << query.rep();
}
练习 15.36:
#ifndef QUERY_BASE_H
#define QUERY_BASE_H
#include"TextQuery.h"
class Query_base
{
friend class Query;
protected:
using line_no = TextQuery::line_no;
virtual ~Query_base() = default;
private:
// eval返回与当前Query匹配的QueryResult
//virtual QueryResult eval(const TextQuery&)const = 0;
// rep是表示查询的一个string
virtual string rep()const = 0;
};
class Query
{
friend Query operator~(const Query&);
friend Query operator|(const Query&, const Query&);
friend Query operator&(const Query&, const Query&);
public:
Query(const string&); // 构建一个新的WordQuery
// 接口函数:调用对应的Query_Base操作
//QueryResult eval(const TextQuery& t)const { return q->eval(t); }
string rep()const
{
cout << "Query::rep() \n";
return q->rep();
}
private:
Query(shared_ptr<Query_base> query) : q(query)
{
cout << "Query::Query(shared_ptr<Query_base> query)\n";
}
shared_ptr<Query_base> q;
};
ostream& operator<<(ostream& os, const Query& query)
{
return os << query.rep();
}
#endif
#ifndef QUERY_DERIVE_H
#define QUERY_DERIVE_H
#include"Query_base.h"
#include<algorithm>
// WordQuery类
class WordQuery : public Query_base
{
friend class Query; // Query使用WordQuery构造函数
WordQuery(const string& s) : query_word(s)
{
cout << "WordQuery::WordQuery(" + s + ")\n";
}
/*virtual QueryResult eval(const TextQuery& t)const
{
return t.query(query_word);
}*/
virtual string rep()const
{
cout << "WodQuery::rep()\n";
return query_word;
}
string query_word;
};
// Query构造函数
inline
Query::Query(const string& s) :q(new WordQuery(s))
{
cout << "Query::Query(const string& s) where s=" + s + "\n";
}
// NotQuery类
class NotQuery : public Query_base
{
friend Query operator~(const Query&);
NotQuery(const Query& q) : query(q) { cout << "NotQuery::NotQuery()\n"; }
//virtual QueryResult eval(const TextQuery&)const;
virtual string rep()const
{
cout << "NotQuery::rep()\n";
return "~(" + query.rep() + ")";
}
Query query; // 保存一个需要对其取反的Query
};
inline
Query operator~(const Query& q)
{
return shared_ptr<Query_base>(new NotQuery(q));
}
// BinaryQuery 类
class BinaryQuery : public Query_base
{
protected:
BinaryQuery(const Query& l, const Query& r, string s) :
lhs(l), rhs(r), opSym(s) {
cout << "BinaryQuery::BinaryQuery() where s=" + s + "\n";
}
// 抽象类:BinaryQuery不定义eval, 继承了该纯虚函数
virtual string rep()const
{
cout << "BinaryQuery::rep()\n";
return "(" + lhs.rep() + opSym + rhs.rep() + ")";
}
Query lhs, rhs; // 左侧和右侧对象
string opSym; // 运算符的名字
};
// AndQuery类
class AndQuery : public BinaryQuery
{
friend Query operator&(const Query&, const Query&);
AndQuery(const Query& l, const Query& r) : BinaryQuery(l, r, "&")
{
cout << "AndQuery::AndQuery()\n";
}
// AndQuery继承了rep并且定义了eval纯虚函数
//virtual QueryResult eval(const TextQuery&)const;
};
inline
Query operator&(const Query& lhs, const Query& rhs)
{
return shared_ptr<Query_base>(new AndQuery(lhs, rhs));
}
// OrQuery类
class OrQuery : public BinaryQuery
{
friend Query operator|(const Query&, const Query&);
OrQuery(const Query& l, const Query& r) : BinaryQuery(l, r, "|")
{
cout << "OrQuery::OrQuery\n";
}
//virtual QueryResult eval(const TextQuery&)const;
};
inline
Query operator|(const Query& lhs, const Query& rhs)
{
return shared_ptr<Query_base>(new OrQuery(lhs, rhs));
}
#endif
#ifndef TEXTQUERY_H
#define TEXTQUERY_H
#include<iostream>
#include<string>
#include<fstream>
#include<memory>
#include<vector>
#include<set>
#include<map>
#include<sstream>
#include<algorithm>
using namespace std;
class QueryResult;
class TextQuery
{
public:
using line_no = vector<string>::size_type;
TextQuery(ifstream&);
QueryResult query(const string&)const;
private:
shared_ptr<vector<string>> file; // 输入文件
// 每个单词到它所在的行号的集合的map
map<string, shared_ptr<set<line_no>>> wm;
};
class QueryResult
{
friend ostream& print(ostream&, const QueryResult&);
public:
using line_no = vector<string>::size_type;
QueryResult(string s, shared_ptr<set<line_no>> plines, shared_ptr<vector<string>> f) :
sought(s), lines(plines), file(f) { }
private:
string sought; // 查询单词
shared_ptr<set<line_no>> lines; // 出现的行号
shared_ptr<vector<string>> file; // 输入文件
};
TextQuery::TextQuery(ifstream& ifs) : file(new vector<string>)
{
line_no lineNo = 0;
for (string line; getline(ifs, line); ++lineNo)
{
file->push_back(line); // 保存此行文本
istringstream line_iss(line);
for (string word; line_iss >> word;)
{
// 避免读入标点符号
auto punct_pos = find_if(word.begin(), word.end(), ispunct);
if (punct_pos != word.end())
word = string(word.begin(), punct_pos);
// 如果单词不在wm中,以之为下标在wm中添加一项
auto& lines = wm[word];
if (!lines) // 第一次遇到这个单词时,此指针为空
lines.reset(new set<line_no>);
lines->insert(lineNo);
}
}
}
QueryResult TextQuery::query(const string& sought)const
{
// 如果未找到sought, 我们将返回指向此set的指针
static shared_ptr<set<line_no>> nodata(new set<line_no>);
auto loc = wm.find(sought);
if (loc != wm.end())
return QueryResult(sought, loc->second, file);
else
return QueryResult(sought, nodata, file);
}
// 打印结果
ostream& print(ostream& os, const QueryResult& qr)
{
os << qr.sought << " occurs " << qr.lines->size()
<< (qr.lines->size() > 1 ? " times" : " time") << endl;
// 打印单词出现的每一行
for (auto line : *qr.lines)
os << "\t(line " << line + 1 << ") " << (*qr.file)[line] << endl;
return os;
}
#endif
#include"Query_base.h"
#include"Query_Derive.h"
#include"TextQuery.h"
int main()
{
Query q = Query("fiery") & Query("bird") | Query("wind");
cout << q;
return 0;
}
//WordQuery::WordQuery(wind)
//Query::Query(const string& s) where s = wind
//WordQuery::WordQuery(bird)
//Query::Query(const string & s) where s = bird
//WordQuery::WordQuery(fiery)
//Query::Query(const string & s) where s = fiery
//BinaryQuery::BinaryQuery() where s = &
//AndQuery::AndQuery()
//Query::Query(shared_ptr<Query_base> query)
//BinaryQuery::BinaryQuery() where s = |
//OrQuery::OrQuery
//Query::Query(shared_ptr<Query_base> query)
//
//Query::rep()
//BinaryQuery::rep()
//Query::rep()
//WodQuery::rep()
//Query::rep()
//BinaryQuery::rep()
//Query::rep()
//WodQuery::rep()
//Query::rep()
//WodQuery::rep()
//((fiery& bird) | wind)
练习 15.37:
练习 15.38:
BinaryQuery a = Query("fiery") & Query("bird");
// 不合法. BinaryQuery是一个抽象类
AndQuery b = Query("fiery") & Query("bird");
// 不合法. 操作符&返回一个Query,不存在从Query到AndQuery的转换
OrQuery c = Query("fiery") & Query("bird");
// 不合法. 操作符&返回一个Query,不存在从Query到OrQuery的转换
练习 15.39:
#ifndef QUERY_BASE_H
#define QUERY_BASE_H
#include"TextQuery.h"
class Query_base
{
friend class Query;
protected:
using line_no = TextQuery::line_no;
virtual ~Query_base() = default;
private:
// eval返回与当前Query匹配的QueryResult
virtual QueryResult eval(const TextQuery&)const = 0;
// rep是表示查询的一个string
virtual string rep()const = 0;
};
class Query
{
friend Query operator~(const Query&);
friend Query operator|(const Query&, const Query&);
friend Query operator&(const Query&, const Query&);
public:
Query(const string&); // 构建一个新的WordQuery
// 接口函数:调用对应的Query_Base操作
QueryResult eval(const TextQuery& t)const { return q->eval(t); }
string rep()const
{
cout << "Query::rep() \n";
return q->rep();
}
private:
Query(shared_ptr<Query_base> query) : q(query)
{
cout << "Query::Query(std::shared_ptr<Query_base> query)\n";
}
shared_ptr<Query_base> q;
};
ostream& operator<<(ostream& os, const Query& query)
{
return os << query.rep();
}
#endif
#ifndef QUERY_DERIVE_H
#define QUERY_DERIVE_H
#include"Query_base.h"
#include<algorithm>
// WordQuery类
class WordQuery : public Query_base
{
friend class Query; // Query使用WordQuery构造函数
WordQuery(const string& s) : query_word(s)
{
cout << "WordQuery::WordQuery(" + s + ")\n";
}
virtual QueryResult eval(const TextQuery& t)const
{
return t.query(query_word);
}
virtual string rep()const
{
cout << "WodQuery::rep()\n";
return query_word;
}
string query_word;
};
// Query构造函数
inline
Query::Query(const string& s) :q(new WordQuery(s))
{
cout << "Query::Query(const string& s) where s=" + s + "\n";
}
// NotQuery类
class NotQuery : public Query_base
{
friend Query operator~(const Query&);
NotQuery(const Query& q) : query(q) { cout << "NotQuery::NotQuery()\n"; }
virtual QueryResult eval(const TextQuery&)const;
virtual string rep()const
{
cout << "NotQuery::rep()\n";
return "~(" + query.rep() + ")";
}
Query query; // 保存一个需要对其取反的Query
};
inline
Query operator~(const Query& q)
{
return shared_ptr<Query_base>(new NotQuery(q));
}
// BinaryQuery 类
class BinaryQuery : public Query_base
{
protected:
BinaryQuery(const Query& l, const Query& r, string s) :
lhs(l), rhs(r), opSym(s) {
cout << "BinaryQuery::BinaryQuery() where s=" + s + "\n";
}
// 抽象类:BinaryQuery不定义eval, 继承了该纯虚函数
virtual string rep()const
{
cout << "BinaryQuery::rep()\n";
return "(" + lhs.rep() + opSym + rhs.rep() + ")";
}
Query lhs, rhs; // 左侧和右侧对象
string opSym; // 运算符的名字
};
// AndQuery类
class AndQuery : public BinaryQuery
{
friend Query operator&(const Query&, const Query&);
AndQuery(const Query& l, const Query& r) : BinaryQuery(l, r, "&")
{
cout << "AndQuery::AndQuery()\n";
}
// AndQuery继承了rep并且定义了eval纯虚函数
virtual QueryResult eval(const TextQuery&)const;
};
inline
Query operator&(const Query& lhs, const Query& rhs)
{
return shared_ptr<Query_base>(new AndQuery(lhs, rhs));
}
// OrQuery类
class OrQuery : public BinaryQuery
{
friend Query operator|(const Query&, const Query&);
OrQuery(const Query& l, const Query& r) : BinaryQuery(l, r, "|")
{
cout << "OrQuery::OrQuery\n";
}
virtual QueryResult eval(const TextQuery&)const;
};
inline
Query operator|(const Query& lhs, const Query& rhs)
{
return shared_ptr<Query_base>(new OrQuery(lhs, rhs));
}
// 返回运算对象查询结果set中不存在的行
QueryResult NotQuery::eval(const TextQuery& text)const
{
// 通过Query运算对象对eval进行虚调用
QueryResult result = query.eval(text);
// 开始时结果set为空
shared_ptr<set<line_no>> ret_lines = make_shared<set<line_no>>();
// 我们必须在运算对象出现的所有行中进行迭代
auto beg = result.begin(), end = result.end();
// 对于输入文件的每一行,如果改行不在result中,则将其添加到set_lines
auto sz = result.get_file()->size();
for (size_t n = 0; n != sz; ++n)
{
if (beg == end || *beg != n)
ret_lines->insert(n); // 如果不在result当中,添加这一行
else if (beg != end)
++beg; // 否则继续获取result的下一行;
}
return QueryResult(rep(), ret_lines, result.get_file());
}
// 返回运算对象查询结果set的交集
QueryResult AndQuery::eval(const TextQuery& text)const
{
// 通过Query运算对象进行的虚调用,以获得运算对象的查询结果set
QueryResult left = lhs.eval(text), right = rhs.eval(text);
// 保存left和right交集的set
shared_ptr<set<line_no>> ret_lines = make_shared<set<line_no>>();
// 将left和right的共同元素写入ret_lines
set_intersection(left.begin(), left.end(), right.begin(), right.end(),
inserter(*ret_lines, ret_lines->begin()));
return QueryResult(rep(), ret_lines, left.get_file());
}
// 返回运算对象查询结果set的并集
QueryResult OrQuery::eval(const TextQuery& text) const
{
// 通过Query成员lhs和rhs进行的虚调用
// 调用eval返回每个运算对象的QueryResult
QueryResult left = lhs.eval(text), right = rhs.eval(text);
// 将左侧运算对象的行号拷贝到结果set中
shared_ptr<set<line_no>> ret_lines = make_shared<set<line_no>>(left.begin(), left.end());
// 插入右侧运算对象所得的行号
ret_lines->insert(right.begin(), right.end());
return QueryResult(rep(), ret_lines, left.get_file());
}
#endif
#ifndef TEXTQUERY_H
#define TEXTQUERY_H
#include"StrBlob.h"
#include<iostream>
#include<fstream>
#include<map>
#include<set>
#include<sstream>
class QueryResult;
class TextQuery
{
public:
using line_no = StrBlob::size_type;
TextQuery(ifstream&);
QueryResult query(const string&)const;
private:
shared_ptr<StrBlob> file; // 输入文件
// 每个单词到它所在的行号的集合的map
map<string, shared_ptr<set<line_no>>> wm;
};
class QueryResult
{
friend ostream& operator<<(ostream&, const QueryResult&);
public:
using ResultIter = set<StrBlob::size_type>::iterator;
using line_no = StrBlob::size_type;
QueryResult(string s, shared_ptr<set<line_no>> plines, shared_ptr<StrBlob> f) :
sought(s), lines(plines), file(f) { }
ResultIter begin() const { return lines->begin(); }
ResultIter end() const { return lines->end(); }
shared_ptr<StrBlob> get_file() const { return file; }
private:
string sought; // 查询单词
shared_ptr<set<line_no>> lines; // 出现的行号
shared_ptr<StrBlob> file; // 输入文件
};
TextQuery::TextQuery(ifstream& ifs) : file(new StrBlob)
{
line_no lineNo = 0;
for (string line; getline(ifs, line); ++lineNo)
{
file->push_back(line); // 保存此行文本
istringstream line_iss(line);
for (string word; line_iss >> word;)
{
// 避免读入标点符号
auto punct_pos = find_if(word.begin(), word.end(), ispunct);
if (punct_pos != word.end())
word = string(word.begin(), punct_pos);
// 如果单词不在wm中,以之为下标在wm中添加一项
auto& lines = wm[word];
if (!lines) // 第一次遇到这个单词时,此指针为空
lines.reset(new set<line_no>);
lines->insert(lineNo);
}
}
}
QueryResult TextQuery::query(const string& sought)const
{
// 如果未找到sought, 我们将返回指向此set的指针
static shared_ptr<set<line_no>> nodata(new set<line_no>);
auto loc = wm.find(sought);
if (loc != wm.end())
return QueryResult(sought, loc->second, file);
else
return QueryResult(sought, nodata, file);
}
// 打印结果
ostream& operator<<(ostream& os, const QueryResult& qr)
{
os << qr.sought << " occurs " << qr.lines->size()
<< (qr.lines->size() > 1 ? " times" : " time") << endl;
// 打印单词出现的每一行
for (auto line : *qr.lines)
{
ConstStrBlobPtr p(*qr.file, line);
os << "\t(line " << line + 1 << ") " << p.deref() << endl;
}
return os;
}
#endif
#ifndef STRBLOB_H
#define STRBLOB_H
#include<iostream>
#include<string>
#include<vector>
#include<memory>
#include<fstream>
using namespace std;
class ConstStrBlobPtr;
// StrBlob类
class StrBlob
{
public:
typedef vector<string>::size_type size_type;
friend class ConstStrBlobPtr;
ConstStrBlobPtr begin()const; // add const
ConstStrBlobPtr end()const; // add const
StrBlob() : data(make_shared<vector<string>>()) { }
StrBlob(initializer_list<string> il) :
data(make_shared<vector<string>>(il)) { }
size_type size()const { return data->size(); }
bool empty()const { return data->empty(); }
void push_back(const string& t) { data->push_back(t); }
void pop_back()
{
check(0, "pop_back on empty StrBlob");
data->pop_back();
}
string& front()
{
check(0, "front on empty StrBlob");
return data->front();
}
const string& front()const
{
check(0, "front on empty StrBlob");
return data->front();
}
string& back()
{
check(0, "back on empty StrBlob");
return data->back();
}
const string& back()const
{
check(0, "back on empty StrBlob");
return data->back();
}
private:
shared_ptr<vector<string>> data;
void check(size_type i, const string& msg)const
{
if (i >= data->size())
throw out_of_range(msg);
}
};
class ConstStrBlobPtr
{
public:
ConstStrBlobPtr() : curr(0) {}
ConstStrBlobPtr(const StrBlob& a, size_t sz = 0) : wptr(a.data), curr(sz) {}
bool operator!=(const ConstStrBlobPtr& p) { return p.curr != curr; }
const string& deref()const
{
auto p = check(curr, "dereference past end");
return (*p)[curr];
}
ConstStrBlobPtr& incr()
{
check(curr, "increment past end of ConstStrBlobPtr");
++curr;
return *this;
}
private:
shared_ptr<vector<string>> check(size_t i, const string& msg)const
{
auto ret = wptr.lock();
if (!ret)
throw runtime_error("unbound ConstStrBlobPtr");
if (i >= ret->size())
throw out_of_range(msg);
return ret;
}
weak_ptr<vector<string>> wptr;
size_t curr;
};
ConstStrBlobPtr StrBlob::begin()const { return ConstStrBlobPtr(*this); }
ConstStrBlobPtr StrBlob::end()const { return ConstStrBlobPtr(*this, data->size()); }
#endif
#include"Query_base.h"
#include"Query_Derive.h"
#include"StrBlob.h"
#include"TextQuery.h"
int main()
{
ifstream file("storyDataFile.txt");
TextQuery tQuery(file);
Query q = Query("fiery") & Query("bird") | Query("wind");
cout << q.eval(tQuery);
return 0;
}
//输出:
//((fiery& bird) | wind) occurs 3 times
//(line 2) Her Daddy says when the wind blows
//(line 4) like a fiery bird in flight.
//(line 5) A beautiful fiery bird, he tells her,
练习 15.40:
eval函数通过make_shared动态地分配一个新的set,所以如果其中任何一个set为空,则不会在这个set中添加任何内容,能够正确处理空集的情况。