cppPrimer学习15th
cppPrimer学习15th
目录
TODO
父类中的静态成员,子类按照public应该公用?
15.1
什么是虚成员?
父类希望子类改写的,定义为 virtual的成员函数
15.2
protected 和 priate的区别
private成员是都不允许外部访问的成员
protected成员允许子类访问的成员
15.3
// 15.3 定义你自己的 Quote 类和 print_total 函数。
#include <string>
#include <iostream>
class Quote
{
private:
std::string bookNo;
protected:
double price = 0;
public:
Quote() = default;
Quote(const std::string &name, double price) : bookNo(name), price(price) {}
virtual ~Quote() = default;
std::string isbn() const { return bookNo; }
virtual double net_price(int n) const { return price * n; }
};
std::ostream &print_total(const Quote &book, int n, std::ostream &o = std::cout)
{
return o << "Isbn= " << book.isbn() << "\tTotal price =" << book.net_price(n);
}
int main(int argc, char const *argv[])
{
Quote baseBook("A1", (double)50);
print_total(baseBook, 10) << std::endl;
while (1)
;
return 0;
}
15.4
下面哪条声明语句是不正确的?请解释原因
class Base { ... };
(a) class Derived : public Derived { ... }; X 不能继承自己
(b) class Derived : private Base { ... }; 这是定义
(c) class Derived : public Base; 可以
15.5
15.6
// 15.6将 Quote 和 Bulk_quote 的对象传给15.2.1节练习中的 print_total 函数,检查该函数是否正确
// 15.5 定义你自己的 Bulk_quote 类。
// 15.3 定义你自己的 Quote 类和 print_total 函数。
#include <string>
#include <iostream>
class Quote
{
private:
std::string bookNo;
protected:
double price = 0;
public:
Quote() = default;
Quote(const std::string &name, double price) : bookNo(name), price(price) {}
virtual ~Quote() = default;
std::string isbn() const { return bookNo; }
virtual double net_price(int n) const { return price * n; }
};
class Bulk_quote : public Quote
{
private:
int min = 0;
double discount = 0;
public:
Bulk_quote() = default;
Bulk_quote(std::string name, double price, double discount, int min) : Quote(name, price), discount(discount), min(min) {}
double net_price(int n) const override
{
return (n < min ? price * n : price * (1 - discount) * n);
// if (n < min)
// {
// return price * n;
// }
// else
// {
// return price * (1 - discount) * n;
// }
}
};
std::ostream &print_total(const Quote &book, int n, std::ostream &o = std::cout)
{
return o << "Isbn= " << book.isbn() << "\tTotal price =" << book.net_price(n);
}
int main(int argc, char const *argv[])
{
Quote baseBook("A1", (double)50);
print_total(baseBook, 10) << std::endl;
Bulk_quote devBook("A2", (double)50, 0.1, 2);
print_total(devBook, 2) << std::endl;
print_total(devBook, 3) << std::endl;
while (1)
;
return 0;
}
15.7
// 15.7 定义一个类使其实现一种数量受限的折扣策略,具体策略是:当购买书籍的数量不超过一个给定的限量时享受折扣,如果购买量一旦超过了限量,则超出的部分将以原价销售。
// 15.5 定义你自己的 Bulk_quote 类。
// 15.3 定义你自己的 Quote 类和 print_total 函数。
#include <string>
#include <iostream>
class Quote
{
private:
std::string bookNo;
protected:
double price = 0;
public:
Quote() = default;
Quote(const std::string &name, double price) : bookNo(name), price(price) {}
virtual ~Quote() = default;
std::string isbn() const { return bookNo; }
virtual double net_price(int n) const { return price * n; }
};
class Bulk_quote : public Quote
{
private:
int min = 0;
double discount = 0;
public:
Bulk_quote() = default;
Bulk_quote(std::string name, double price, double discount, int min) : Quote(name, price), discount(discount), min(min) {}
double net_price(int n) const override
{
return (n < min ? price * n : price * (1 - discount) * n);
}
};
class Bulk_quote2 : public Quote
{
private:
int min = 0;
int max = 0;
double discount = 0;
public:
Bulk_quote2() = default;
Bulk_quote2(std::string name, double price, double discount, int min, int max) : Quote(name, price), discount(discount), min(min), max(max) {}
double net_price(int n) const override
{
if (n < min)
return price * n;
else if (n <= max)
return price * (1 - discount) * n;
else
return price * (1 - discount) * max + (n - max) * price;
}
};
std::ostream &print_total(const Quote &book, int n, std::ostream &o = std::cout)
{
return o << "Isbn= " << book.isbn() << "\tTotal price =" << book.net_price(n);
}
int main(int argc, char const *argv[])
{
Quote baseBook("A1", (double)50);
print_total(baseBook, 10) << std::endl;
Bulk_quote devBook("A2", (double)50, 0.1, 2);
print_total(devBook, 2) << std::endl;
print_total(devBook, 3) << std::endl;
Bulk_quote2 devBook2("A3", (double)50, 0.1, 2, 5);
print_total(devBook2, 1) << std::endl;
print_total(devBook2, 3) << std::endl;
print_total(devBook2, 10) << std::endl;
while (1)
;
return 0;
}
15.8
// 15.8 给出静态类型和动态类型的定义
静态类型: 在编译时已知,他是变量声明时的类型或表达式生成的类型
动态类型: 表示的是内存中的对象类型,直到运行时才确定
15.9
在什么情况下表达式的静态类型可能与动态类型不同?请给出三个静态类型与动态类型不同的例子。
基类的指针类型或者引用类型都可以动态绑定,即静态类型为基类,动态类型为实际类
A pointer or reference to a base-class type can refer to an to object of derived type.
In such cases the static type is reference (or pointer) to base,
but the dynamic type is reference (or pointer) to derived.
Anything like this can be an example.
// 定义一个子类
Bulk_quote bulk_quote("bulk_quote_1", 10.10, 10, 0.5);
// 定义一个基类的指针指向了子类的内存
Quote *quote_pointer = &bulk_quote;
quote_pointer->net_price(5);
// 定义了一个基类的引用指向子类
Quote "e_reference = bulk_quote;
quote_reference.net_price(5);
15.10
回忆我们在8.1节进行的讨论,
解释第284页中将 ifstream 传递给 Sales_data 的read 函数的程序是如何工作的。
read的参数是 istream, 我们传递的是fstream,他是istream的子类,
15.11
//15.11 为你的 Quote 类体系添加一个名为 debug 的虚函数,令其分别显示每个类的数据成员
// 15.7 定义一个类使其实现一种数量受限的折扣策略,具体策略是:当购买书籍的数量不超过一个给定的限量时享受折扣,如果购买量一旦超过了限量,则超出的部分将以原价销售。
// 15.5 定义你自己的 Bulk_quote 类。
// 15.3 定义你自己的 Quote 类和 print_total 函数。
#include <string>
#include <iostream>
class Quote
{
private:
std::string bookNo;
protected:
double price = 0;
public:
Quote() = default;
Quote(const std::string &name, double price) : bookNo(name), price(price) {}
virtual ~Quote() = default;
std::string isbn() const { return bookNo; }
virtual double net_price(int n) const { return price * n; }
virtual void debug(std::ostream &o = std::cout)
{
o << "BookBo= " << isbn() << " price=" << price << std::endl;
}
};
class Bulk_quote : public Quote
{
private:
int min = 0;
double discount = 0;
public:
Bulk_quote() = default;
Bulk_quote(std::string name, double price, double discount, int min) : Quote(name, price), discount(discount), min(min) {}
double net_price(int n) const override
{
return (n < min ? price * n : price * (1 - discount) * n);
}
void debug(std::ostream &o = std::cout) override
{
o << "BookBo= " << isbn() << " price=" << price << " min=" << min << "discount= " << discount << std::endl;
}
};
class Bulk_quote2 : public Quote
{
private:
int min = 0;
int max = 0;
double discount = 0;
public:
Bulk_quote2() = default;
Bulk_quote2(std::string name, double price, double discount, int min, int max) : Quote(name, price), discount(discount), min(min), max(max) {}
double net_price(int n) const override
{
if (n < min)
return price * n;
else if (n <= max)
return price * (1 - discount) * n;
else
return price * (1 - discount) * max + (n - max) * price;
}
void debug(std::ostream &o = std::cout) override
{
o << "BookBo= " << isbn() << " price=" << price << " min=" << min << " max= " << max << " discount= " << discount << std::endl;
}
};
std::ostream &print_total(const Quote &book, int n, std::ostream &o = std::cout)
{
return o << "Isbn= " << book.isbn() << "\tTotal price =" << book.net_price(n);
}
int main(int argc, char const *argv[])
{
Quote baseBook("A1", (double)50);
print_total(baseBook, 10) << std::endl;
baseBook.debug();
Bulk_quote devBook("A2", (double)50, 0.1, 2);
print_total(devBook, 2) << std::endl;
print_total(devBook, 3) << std::endl;
devBook.debug();
Bulk_quote2 devBook2("A3", (double)50, 0.1, 2, 5);
print_total(devBook2, 1) << std::endl;
print_total(devBook2, 3) << std::endl;
print_total(devBook2, 10) << std::endl;
devBook2.debug();
while (1)
;
return 0;
}
15.12
// 15.12有必要将一个成员函数同时声明成 override 和 final 吗?为什么?
有, override 是针对 virtual
final 针对的是是否能被继承接着改写
15.13
// 15.13 给定下面的类,解释每个 print 函数的机理:在上述代码中存在问题吗?如果有,你该如何修改它?
class base {
public:
string name() { return basename;}
virtual void print(ostream &os) { os << basename; }
private:
string basename;
};
class derived : public base {
public:
void print(ostream &os) { print(os); os << " " << i; }
// void print(ostream &os) { this->base::print(os); os << " " << i; }
private:
int i;
};
问题,子类的 print递归调用自己,没有终止条件
修改: 子类调用父类的print
// 15.13 给定下面的类,解释每个 print 函数的机理:在上述代码中存在问题吗?如果有,你该如何修改它?
#include <iostream>
#include <string>
using namespace std;
class base
{
public:
string name() { return basename; }
virtual void print(ostream &os) { os << basename; }
private:
string basename;
};
class derived : public base
{
public:
//void print(ostream &os) { print(os); os << " " << i; }
void print(ostream &os)
{
//this->base::print(os);
base::print(os);
os << " " << i;
}
private:
int i;
};
int main(int argc, char const *argv[])
{
derived A;
A.print(std::cout);
while (1)
;
return 0;
}
15.14
// 15.14 给定上一题中的类以及下面这些对象,说明在运行时调用哪个函数:
base bobj; base *bp1 = &bobj; base &br1 = bobj;
derived dobj; base *bp2 = &dobj; base &br2 = dobj;
(a) bobj.print(); 基类
(b) dobj.print(); 子类
(c) bp1->name(); 基类
(d) bp2->name(); 基类的,不是虚函数
(e) br1.print(); 基类 动态绑定
(f) br2.print(); 子类 动态绑定
15.15
15.16
// 15.16 改写你在15.2.2节练习中编写的数量受限的折扣策略,令其继承 Disc_quote。
// 15.15 定义你自己的 Disc_quote 和 Bulk_quote。
// 15.11 为你的 Quote 类体系添加一个名为 debug 的虚函数,令其分别显示每个类的数据成员
// 15.7 定义一个类使其实现一种数量受限的折扣策略,具体策略是:当购买书籍的数量不超过一个给定的限量时享受折扣,如果购买量一旦超过了限量,则超出的部分将以原价销售。
// 15.5 定义你自己的 Bulk_quote 类。
// 15.3 定义你自己的 Quote 类和 print_total 函数。
#include <string>
#include <iostream>
class Quote
{
private:
std::string bookNo;
protected:
double price = 0;
public:
Quote() = default;
Quote(const std::string &name, double price) : bookNo(name), price(price) {}
virtual ~Quote() = default;
std::string isbn() const { return bookNo; }
virtual double net_price(int n) const { return price * n; }
virtual std::ostream &debug(std::ostream &o = std::cout) const
{
return o << "BookBo= " << isbn() << " price=" << price;
}
};
class Disc_quote : public Quote
{
protected:
double discount = 0;
int quantity = 0;
public:
Disc_quote() = default;
Disc_quote(const std::string &name, double price, int num, double discount) : Quote(name, price), quantity(num), discount(discount) {}
virtual double net_price(int n) const = 0;
std::ostream &debug(std::ostream &o = std::cout) const override
{
return Quote::debug(o) << " discount= " << discount << " quantity=" << quantity;
}
};
class Bulk_quote : public Disc_quote
{
public:
Bulk_quote() = default;
Bulk_quote(const std::string &name, double price, int num, double discount) : Disc_quote(name, price, num, discount) {}
double net_price(int n) const override
{
if (n <= quantity)
return price * n;
else
return price * quantity + (n - quantity) * price * (1 - discount);
}
};
class Bulk_quote2 : public Disc_quote
{
protected:
int quantity_lv2 = 0; //享受折扣的最大数量
public:
Bulk_quote2() = default;
Bulk_quote2(const std::string &name, double price, int num, double discount, int lv2) : Disc_quote(name, price, num, discount), quantity_lv2(lv2) {}
double net_price(int n) const override
{
int tmp = quantity_lv2 + quantity;
if (n <= quantity)
return price * n;
else if (n < tmp)
return price * quantity + (tmp - n) * price * (1 - discount);
else
return price * quantity + quantity_lv2 * price * (1 - discount) + (n - quantity_lv2 - quantity) * price;
}
std::ostream &debug(std::ostream &o = std::cout) const override
{
return Disc_quote::debug(o) << " quantity_lv2= " << quantity_lv2;
}
};
std::ostream &print_total(const Quote &book, int n, std::ostream &o = std::cout)
{
return o << "Isbn= " << book.isbn() << "\tTotal price =" << book.net_price(n);
}
int main(int argc, char const *argv[])
{
Quote baseBook("A1", (double)50);
print_total(baseBook, 10) << std::endl;
baseBook.debug() << std::endl;
Bulk_quote devBook("A2", (double)50, 5, 0.1);
print_total(devBook, 2) << std::endl;
print_total(devBook, 6) << std::endl;
devBook.debug() << std::endl;
Bulk_quote2 devBook2("A2", (double)50, 5, 0.1, 3);
print_total(devBook2, 2) << std::endl;
print_total(devBook2, 10) << std::endl;
devBook2.debug() << std::endl;
while (1)
;
return 0;
}
15.17
// 15.17 尝试定义一个 Disc_quote 的对象,看看编译器给出的错误信息是什么?
error: invalid abstract return type 'Disc_quote'
because the following virtual functions are pure within 'Disc_quote':
virtual double net_price(int n) const = 0;
15.18
假设给定了第543页和第544页的类,同时已知每个对象的类型如注释所示,判断下面的哪些赋值语句是合法的。
解释那些不合法的语句为什么不被允许:
Base *p = &d1; //d1 的类型是 Pub_Derv --------------ok
p = &d2; //d2 的类型是 Priv_Derv
p = &d3; //d3 的类型是 Prot_Derv
p = &dd1; //dd1 的类型是 Derived_from_Public ---ok
p = &dd2; //dd2 的类型是 Derived_from_Private
p = &dd3; //dd3 的类型是 Derived_from_Protected
只有是共有继承,才允许子类到父类的转换
15.19
假设543页和544页的每个类都有如下形式的成员函数:
对于每个类,分别判断上面的函数是否合法。
void memfcn(Base &b) { b = *this; }
// 这里考察的是 继承描述符,也就是 子类的public/protected ,孙子类才能访问基类
// 而子类本身没有限制
Base类:合法
Pub_Derv类:合法 子类都合法
Priv_Derv类:合法
Prot_Derv类:合法
Derived_from_Public类:合法
Derived_from_Private类:非法
Derived_from_Protected类:合法
15.20
// 编写代码检验你对前面两题的回答是否正确。
#include <iostream>
#include <string>
using namespace std;
class Base
{
public:
void memfcn(Base &b) { b = *this; };
protected:
int prot_mem;
private:
char priv_mem;
};
struct Pub_Derv : public Base
{
public:
void memfcn(Base &b) { b = *this; };
};
struct Priv_Derv : private Base
{
public:
void memfcn(Base &b) { b = *this; };
};
struct Prot_Derv : protected Base
{
public:
void memfcn(Base &b) { b = *this; };
};
struct Derived_from_Public : public Pub_Derv
{
public:
void memfcn(Base &b) { b = *this; };
};
struct Derived_from_Private : public Priv_Derv
{
public:
// void memfcn(Base &b) { b = *this; };
};
struct Derived_from_Protected : public Prot_Derv
{
public:
void memfcn(Base &b) { b = *this; };
};
int main(int argc, char const *argv[])
{
// 只有当D公有地继承B时,用户代码才能使用派生类向基类的转换。
Pub_Derv d1; //public 继承 base
Priv_Derv d2; //private 继承
Prot_Derv d3; //protected 继承
Derived_from_Public dd1; //公共继承 公共继承的派生类 父->public子->public孙
Derived_from_Private dd2; //公共继承 私有继承的派生类 父->private子->public孙
Derived_from_Protected dd3; //公共继承 公共继承的派生类 父->protected子->public孙
Base *p = &d1; //d1 的类型是 Pub_Derv // inaccessible=无法访问
//p = &d2; //d2 的类型是 Priv_Derv //error: 'Base' is an inaccessible base of 'Priv_Derv'
//p = &d3; //d3 的类型是 Prot_Derv //error: 'Base' is an inaccessible base of 'Prot_Derv'
p = &dd1; //dd1 的类型是 Derived_from_Public
//p = &dd2; //dd2 的类型是 Derived_from_Private //error: 'Base' is an inaccessible base of 'Derived_from_Private'
//p = &dd3; //dd3 的类型是 Derived_from_Protected //error: 'Base' is an inaccessible base of 'Derived_from_Protected'
return 0;
}
15.23
//15.23 假设第550页的 D1 类需要覆盖它继承而来的 fcn 函数,你应该如何对其进行修改? -- 这里是虚函数的覆盖,要改变参数类型为void,然后加overide检查
// 如果你修改之后 fcn 匹配了 Base 中的定义,则该节的那些调用语句将如何解析?
// http://stackoverflow.com/questions/21320779/trying-to-understand-dynamic-binding-and-virtual-functions
#include <iostream>
#include <string>
class Base
{
public:
virtual int fcn()
{
std::cout << "Base::fcn()\n";
return 0;
}
};
class D1 : public Base
{
public:
int fcn() override
{
std::cout << "D1::fcn()\n";
return 0;
}
//! ^^^--fixed to override the inherited version
virtual void f2() { std::cout << "D1::f2()\n"; }
};
class D2 : public D1
{
public:
int fcn(int);
int fcn() override
{
std::cout << "D2::fcn()\n";
return 0;
}
void f2() override { std::cout << "D2::f2()\n"; }
};
int main()
{
Base bobj;
D1 d1obj;
D2 d2obj;
Base *bp1 = &bobj, *bp2 = &d1obj, *bp3 = &d2obj;
// 基类的fcn 是虚函数了,所以都是动态绑定
//bp1->fcn(); //! virtual call, will call Base::fcn at run time
//bp2->fcn(); //! virtual call, will call D1::fcn at run time
//bp3->fcn(); //! virtual call, will call D2::fcn at run time
D1 *d1p = &d1obj;
D2 *d2p = &d2obj;
// 父类没有这个方法,虽然是动态绑定,但是动态绑定的第一步是指针有这个方法
//bp2->f2();
//!^^^^^^^^^^^^
//! @note You are calling virtual member functions via a pointer
//! to Base. That means that you can only call methods that exist
//! in the Base class. You cannot simply add methods to a type
//! dynamically.
// 虚函数是从子类开始的,所以后续可以动态绑定了
d1p->f2(); //! virtual call, will call D1::f2() at run time
d2p->f2(); //! virtual call, will call D2::f2() at run time
return 0;
}
15.23.2
// 自己写一个 重载基类的函数名
#include <iostream>
#include <string>
using namespace std;
class base
{
public:
void p() { cout << "base" << endl; }
void p(int i) { cout << "base int" << endl; }
};
class dev1 : public base
{
public:
using base::p; // 关键是这一句
void p(int i) { cout << "dev1 int" << endl; }
};
int main(int argc, char const *argv[])
{
dev1 d1;
d1.p(); // base
d1.p(1); // d1
d1.base::p(1); //base
while (1)
;
return 0;
}
15.24
哪种类需要虚析构函数?虚析构函数必须执行什么样的操作?
基类需要虚析构函数。如果一个类定义了
当一个基类的指针释放时,允许调用子类实际的析构函数释放内存
15.25
我们为什么为 Disc_quote 定义一个默认构造函数?
如果去掉该构造函数的话会对 Bulk_quote 的行为产生什么影响?
1. 不定义的话无法使用默认构造函数,因为自己已经定义了一个其他版本
因为去掉的话,子类无法使用默认的构造函数
15.26
// 15.26 定义 Quote 和 Bulk_quote 的拷贝控制成员,令其与合成的版本行为一致。
//为这些成员以及其他构造函数添加打印状态的语句,使得我们能够知道正在运行哪个程序。
//使用这些类编写程序,预测程序将创建和销毁哪些对象。
//重复实验,不断比较你的预测和实际输出结果是否相同,直到预测完全准确再结束。
#include <iostream>
#include <string>
using namespace std;
class Quote
{
private:
string bookNo;
protected:
double price = 0;
public:
// 默认构造函数
Quote() { std::cout << "Quote()" << std::endl; };
// 正常使用的构造函数
Quote(string name, double p) : bookNo(name), price(p)
{
std::cout << "Quote(string name, double p)" << std::endl;
};
// 拷贝构造
Quote(const Quote &s) : bookNo(s.bookNo), price(s.price)
{
std::cout << "Quote(const Quote &s)" << std::endl;
};
// 移动构造
Quote(Quote &&s) noexcept : bookNo(std::move(s.bookNo)), price((std::move(s.price)))
{
std::cout << "Quote(Quote &&s)" << std::endl;
};
// 拷贝赋值
Quote &operator=(const Quote &s)
{
std::cout << "Quote &operator=(const Quote &s)" << std::endl;
price = s.price;
bookNo = s.bookNo;
return *this;
}
// 移动赋值
Quote &operator=(Quote &&s) noexcept
{
std::cout << "Quote &operator=(Quote &&s)" << std::endl;
price = std::move(s.price);
bookNo = std::move(s.bookNo);
return *this;
}
virtual ~Quote()
{
cout << "Quote Destructor" << endl;
}
string isbn() const { return bookNo; }
};
class Bulk_quote : public Quote
{
protected:
size_t min_qty = 0;
double discount = 0.0;
public:
Bulk_quote()
{
cout << "Bulk_quote()" << endl;
}
Bulk_quote(const string &b, double p, size_t q, double d) : Quote(b, p), min_qty(q), discount(d)
{
cout << " Bulk_quote(const string &b, double p, size_t q, double d)" << endl;
}
Bulk_quote(const Bulk_quote &rhs) : Quote(rhs), min_qty(rhs.min_qty), discount(rhs.discount)
{
cout << "Bulk_quote(const Bulk_quote &rhs)" << endl;
}
Bulk_quote &operator=(const Bulk_quote &rhs)
{
cout << "Bulk_quote &operator=(const Bulk_quote &rhs)" << endl;
Quote::operator=(rhs);
min_qty = rhs.min_qty;
discount = rhs.discount;
return *this;
}
Bulk_quote(Bulk_quote &&rhs) noexcept : Quote(rhs), min_qty(std::move(rhs.min_qty)),
discount(std::move(rhs.discount))
{
cout << "Bulk_quote(Bulk_quote &&rhs)" << endl;
}
Bulk_quote &operator=(Bulk_quote &&rhs) noexcept
{
cout << "Bulk_quote &operator=(Bulk_quote &&rhs)" << endl;
Quote::operator=(rhs);
min_qty = std::move(rhs.min_qty);
discount = std::move(rhs.discount);
return *this;
}
virtual ~Bulk_quote()
{
cout << "~Bulk_quote()" << endl;
}
};
int main(int argc, char const *argv[])
{
{
Bulk_quote d1;
std::cout << "-----------------Quote b1(d1)--------------" << std::endl;
Quote b1(d1);
std::cout << "----------------return---------------" << std::endl;
}
while (1)
;
return 0;
}
15.27
using Quote::Quote;
15.28
15.28 定义一个存放 Quote 对象的 vector,将 Bulk_quote 对象传入其中。
计算 vector 中所有元素总的 net_price。
//// 这里的实际是base的没有折扣的价格,push_back传的是子类转换到基类的 拷贝构造 见15.29答案
15.29
//15.29 再运行一次你的程序,这次传入 Quote 对象的 shared_ptr 。
// 如果这次计算出的总额与之前的不一致,解释为什么;如果一直,也请说明原因。
// ask // 调用的实际的net_price
int main(int argc, char const *argv[])
{
Quote baseBook("A1", (double)50);
Bulk_quote devBook("A2", (double)50, 0, 0.1);
vector<Quote> total;
total.push_back(devBook);
total.push_back(devBook);
double sum = 0;
for (auto ch : total)
{
// 这里的实际是base的没有折扣的价格,push_back传的是子类转换到基类的 拷贝构造
sum += ch.net_price(5);
}
std::cout << sum << std::endl;
vector<shared_ptr<Quote>> total_ptr;
total_ptr.push_back(make_shared<Quote>(devBook)); //这里会用子类构造基类,也就是实际还是基类
total_ptr.push_back(make_shared<Bulk_quote>(devBook));
sum = 0;
for (auto ch : total_ptr)
{
// 调用的实际的net_price
sum += ch->net_price(5);
}
std::cout << sum << std::endl;
while (1)
;
return 0;
}
15.30
// 15.30编写你自己的 Basket 类,用它计算上一个练习中交易记录的总价格。
//15.29 再运行一次你的程序,这次传入 Quote 对象的 shared_ptr 。如果这次计算出的总额与之前的不一致,解释为什么;如果一直,也请说明原因。
//15.28 定义一个存放 Quote 对象的 vector,将 Bulk_quote 对象传入其中。计算 vector 中所有元素总的 net_price。
#include <vector>
#include <string>
#include <iostream>
#include <memory>
#include <set>
using namespace std;
// 15.16 改写你在15.2.2节练习中编写的数量受限的折扣策略,令其继承 Disc_quote。
// 15.15 定义你自己的 Disc_quote 和 Bulk_quote。
// 15.11 为你的 Quote 类体系添加一个名为 debug 的虚函数,令其分别显示每个类的数据成员
// 15.7 定义一个类使其实现一种数量受限的折扣策略,具体策略是:当购买书籍的数量不超过一个给定的限量时享受折扣,如果购买量一旦超过了限量,则超出的部分将以原价销售。
// 15.5 定义你自己的 Bulk_quote 类。
// 15.3 定义你自己的 Quote 类和 print_total 函数。
class Quote
{
private:
std::string bookNo;
protected:
double price = 0;
public:
Quote() = default;
Quote(const std::string &name, double price) : bookNo(name), price(price) {}
virtual ~Quote() = default;
std::string isbn() const { return bookNo; }
virtual double net_price(int n) const { return price * n; }
virtual std::ostream &debug(std::ostream &o = std::cout) const
{
return o << "BookBo= " << isbn() << " price=" << price;
}
virtual Quote *clone() const & { return new Quote(*this); }
virtual Quote *clone() && { return new Quote(std::move(*this)); }
};
class Disc_quote : public Quote
{
protected:
double discount = 0;
int quantity = 0;
public:
Disc_quote() = default;
Disc_quote(const std::string &name, double price, int num, double discount) : Quote(name, price), quantity(num), discount(discount) {}
virtual double net_price(int n) const = 0;
std::ostream &debug(std::ostream &o = std::cout) const override
{
return Quote::debug(o) << " discount= " << discount << " quantity=" << quantity;
}
};
class Bulk_quote : public Disc_quote
{
public:
Bulk_quote() = default;
Bulk_quote(const std::string &name, double price, int num, double discount) : Disc_quote(name, price, num, discount) {}
double net_price(int n) const override
{
if (n <= quantity)
return price * n;
else
return price * quantity + (n - quantity) * price * (1 - discount);
}
virtual Bulk_quote *clone() const & { return new Bulk_quote(*this); }
virtual Bulk_quote *clone() && { return new Bulk_quote(std::move(*this)); }
};
double print_total(const Quote &book, int n, std::ostream &o = std::cout)
{
o << "Isbn= " << book.isbn() << "\tTotal price =" << book.net_price(n);
return book.net_price(n);
}
class Basket
{
private:
static bool compare(const shared_ptr<Quote> a, const shared_ptr<Quote> b)
{
return a->isbn() < b->isbn();
}
multiset<shared_ptr<Quote>, decltype(compare) *> items{compare};
public:
//void add_item(const shared_ptr<Quote> s) { items.insert(s); }
void add_item(const Quote &s) { items.insert(std::shared_ptr<Quote>(s.clone())); }
void add_item(Quote &&s) { items.insert(std::shared_ptr<Quote>(std::move(s).clone())); }
double total_show(std::ostream &o = std::cout)
{
double sum = 0;
for (auto it = items.begin(); it != items.end(); it = items.upper_bound(*it))
{
sum += print_total(**it, items.count(*it), o);
}
o << " Total sale:" << sum << endl;
}
};
int main(int argc, char const *argv[])
{
Quote baseBook("A1", (double)50);
Bulk_quote devBook("A2", (double)50, 0, 0.1);
vector<Quote> total;
total.push_back(devBook);
total.push_back(devBook);
double sum = 0;
for (auto ch : total)
{
// 这里的实际是base的没有折扣的价格,push_back传的是子类转换到基类的 拷贝构造
sum += ch.net_price(1);
}
std::cout << sum << std::endl;
vector<shared_ptr<Quote>> total_ptr;
std::shared_ptr<Bulk_quote> pdv1 = std::make_shared<Bulk_quote>(devBook); //total =50*0.9+50*0.9=90 子类的正确调用
total_ptr.push_back(pdv1);
total_ptr.push_back(pdv1);
// 但是下面的调用 ,使用时 devBook来构造Quote类,所以调用的是基类的哦
//total_ptr.push_back(make_shared<Quote>(devBook)); //=50+50=100
sum = 0;
for (auto ch : total_ptr)
{
// 调用的实际的net_price
sum += ch->net_price(1);
}
std::cout << sum << std::endl;
{
vector<shared_ptr<Quote>> total_ptr;
std::shared_ptr<Quote> pdv1 = std::make_shared<Quote>(devBook); //total =50*0.9+50*0.9=90 子类的正确调用
total_ptr.push_back(pdv1);
total_ptr.push_back(pdv1);
sum = 0;
for (auto ch : total_ptr)
{
// 调用的实际的net_price
sum += ch->net_price(1);
}
std::cout << sum << std::endl;
}
//*****************************
Basket vb;
vb.add_item(devBook);
vb.add_item(devBook);
vb.total_show();
while (1)
;
return 0;
}
15.31
// 15.31 已知 s1、s2、s3 和 s4 都是 string,判断下面的表达式分别创建了什么样的对象:
&的优先级高于|
(a) Query(s1) | Query(s2) & ~Query(s3);
wordQuery() | ( wordQuery() & NotQuery)
wordQuery() | AndQuery
OrQuery
(b) Query(s1) | ( Query(s2) & ~Query(s3) );
wordQuery() | ( wordQuery()& NotQuery )
wordQuery() | AndQuery
OrQuery
(c) (Query(s1) & (Query(s2)) | (Query(s3) & Query(s4)));
AndQuery | AndQuery
OrQuery
15.32
当一个 Query 类型的对象被拷贝、移动、赋值或销毁时,将分别发生什么?
数据成员只有一个shared_ptr
拷贝构造: 共享指针+1
移动构造: 共享指针-1,原来的对象指向nullptr
销毁: 计数-1
拷贝赋值: 原来的对象计数-1,待拷贝的+1
移动赋值: 原来的对象-1,待拷贝的不变,右边被拷贝的指向null_ptr
当计数为0,内存销毁
15.33
当一个 Query_base 类型的对象被拷贝、移动赋值或销毁时,将分别发生什么?
抽象类的对象一定实际上是派生类的操作
15.34
针对图15.3构建的表达式:
(a) 例举出在处理表达式的过程中执行的所有构造函数。
(b) 例举出 cout << q 所调用的 rep。
(c) 例举出 q.eval() 所调用的 eval。
a)
Query q = Query("fiery") & Query("bird") | Query("wind")
构造函数:
Query("fiery"),Query("bird"), Query("wind")
1. 执行 Query(const std::string &s)
1.1.调用 WordQuery(const std::string &s)
Query("fiery") & Query("bird") 执行
2. AndQuery(const Query &left, const Query &right)
2.1 调用 BinaryQuery(const Query &l, const Query &r, std::string s)
2.2 return 返回时调用 Query(std::shared_ptr<Query_base> query)
| Query("wind")
3. OrQuery(const Query &left, const Query &right);
3.1 调用 BinaryQuery(const Query &l, const Query &r, std::string s)
3.2 return 返回时调用 Query(std::shared_ptr<Query_base> query)
最后拷贝构造
Query(std::shared_ptr<Query_base> query)
代码的答案为
WordQuery::WordQuery(wind)
Query::Query(const std::string& s) where s=wind
WordQuery::WordQuery(bird)
Query::Query(const std::string& s) where s=bird
WordQuery::WordQuery(fiery)
Query::Query(const std::string& s) where s=fiery
BinaryQuery::BinaryQuery() where s=&
AndQuery::AndQuery()
Query::Query(std::shared_ptr<Query_base> query)
BinaryQuery::BinaryQuery() where s=|
OrQuery::OrQuery
Query::Query(std::shared_ptr<Query_base> query)
b,c) q最后由 OrQuery 构造而来,调用 OrQuery.rep 和 OrQuery.eval
1. 所以rep是 Query.rep 非虚函数
掉用自身成员的 q->rep
这个q是 OrQuery 是虚函数调用
查找到 OrQuery的虚调用
代码答案
Follow <<q
Query::rep()
OrQuery::rep()
BinaryQuery::rep()
Query::rep()
WodQuery::rep()
Query::rep()
AndQuery::rep()
BinaryQuery::rep()
Query::rep()
WodQuery::rep()
Query::rep()
WodQuery::rep()
((fiery & bird) | wind)
15.35
15.36
见github
我在orQuery 和 andQuery增加了rep
std::string rep() const override
{
std::cout << "AndQuery::rep()\n";
return BinaryQuery::rep();
}
std::string rep() const override
{
std::cout << "orQuery::rep()\n";
return BinaryQuery::rep();
}
15.37
如果在派生类中含有 shared_ptr<Query_base> 类型的成员而非 Query 类型的成员,
则你的类需要做出怎样的改变?
Query_base 不能是纯虚函数,需要定义操作
接口类实际就是Query_base,也就是参数是 Query_base
15.38
下面的声明合法吗?如果不合法,请解释原因;如果合法,请指出该声明的含义。
BinaryQuery a = Query("fiery") & Query("bird"); 不合法,BinaryQuery是抽象类,应该定义为 andQuery
AndQuery b = Query("fiery") & Query("bird"); 不合法,没有构造函数,右侧是Query
OrQuery c = Query("fiery") & Query("bird"); 不合法,没有构造函数,右侧是Query
15.39
TextQuery tQuery(file);
Query q = Query("fieryzzz") | Query("wind");
std::cout << "Follow" << std::endl;
std::cout << q.eval(tQuery);
// 这个拆解为 q.eval(tQuery); 以及 operator<<(std::ostream &os, const QueryResult &qr)
---------------------------------------------------------------
WordQuery::WordQuery(wind)
Query::Query(const std::string& s) where s=wind
WordQuery::WordQuery(fieryzzz)
Query::Query(const std::string& s) where s=fieryzzz
BinaryQuery::BinaryQuery() where s=|
OrQuery::OrQuery
Query::Query(std::shared_ptr<Query_base> query)
----------------------------------------------------------------
Follow
OrQuery::rep()
BinaryQuery::rep()
Query::rep()
WodQuery::rep()
Query::rep()
WodQuery::rep()
// 这是是 rep 的打印
(fieryzzz | wind) occurs 1 times
15.40
空集没有关系, 返回的是指针而已 std::make_shared<std::set<line_no>>()
15.41
15.42.b
class QueryHistory
{
public:
Query& operator[](size_t n)
{
return *(query_vec[n]);
}
//return the assigned number of the new query
size_t add_query(const Query&);
private:
vector<shared_ptr<Query>> query_vec;
};
int main()
{
ifstream fin("test.txt");
TextQuery text(fin);
QueryHistory history;
Query q0("Alice");
Query q1("hair");
Query q2("Daddy");
history.add_query(q0);
history.add_query(q1);
history[0] = history[0] | q2;
auto result = history[0].eval(text);
print(cout, result);
return 0;
}
15.42.c
int main()
{
ifstream fin("test.txt");
TextQuery text(fin);
auto q = ~Query("Alice");
auto result = q.eval(text);
print(cout, result);
cout << endl;
print(cout, result, -3, 5);
cout << endl;
print(cout, result, 3, 5);
cout << endl;
print(cout, result, 3, 20);
cout << endl;
return 0;
}
#include "queryresult.h"
#include <iostream>
using std::ostream;
ostream&
print(ostream &os, const QueryResult &qr)
{
os <<"The result of your query "<< qr.sought <<" is: \n";
for (const auto &index: *qr.lines)
os << "\t(line " << index + 1 << ")"
<< *(qr.file->begin() + index) << "\n";
return os;
}
/*
Head is the first line of the range.
Trail is the last line of the range.
*/
ostream&
print(ostream& os, const QueryResult &qr,size_t head, size_t trail)
{
if (head > trail)
{
os << "illegal range!\n";
return os;
}
else
{
os << "The result of your query " << qr.sought << " is: \n";
for (const auto &index : *qr.lines)
{
if (index + 1 >= head&&index + 1 <= trail)
{
os << "\t(line " << index + 1 << ")"
<< *(qr.file->begin() + index) << "\n";
}
}
return os;
}
}