第十五章 面向对象程序设计
15.1
虚函数:基类的成员函数,并在其前面添加关键字virtual,此类函数是基类希望其派生类进行覆盖的函数
15.2
protected:对应受保护成员,派生类可以访问该成员,但其他用户则禁止访问该成员
15.3
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Quote { 2 public: 3 Quote() = default; 4 Quote(const string &book, double sales_price): bookNo(book), price(sales_price) {} 5 string isbn() const { return bookNo; } 6 virtual double net_price(size_t n) const { return n * price; } 7 virtual ~Quote() = default; 8 private: 9 string bookNo; 10 protected: 11 double price = 0.0; 12 }; 13 14 double print_total(ostream &os, const Quote &item, size_t n) 15 { 16 double ret = item.net_price(n); 17 os << "ISBN: " << item.isbn() << " # sold: " << n << " total due: " << ret << endl; 18 return ret; 19 }
15.4
(a):错误,不可以继承自己
(b):正确
(c):错误,派生类的声明不需要包含它的派生列表
15.5
class Bulk_quote : public Quote { public: Bulk_quote() = default; Bulk_quote(const string &book, double sales_price, size_t n, double dis): Quote(book, sales_price), min_qty(n), discount(dis) {} double net_price(size_t n) const override; private: size_t min_qty = 0; double discount = 0.0; }; double Bulk_quote::net_price(size_t n) const { if (n >= min_qty) return n * (1 - discount) * price; else return n * price; }
15.6
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 class Quote { 7 public: 8 Quote() = default; 9 Quote(const string &book, double sales_price): bookNo(book), price(sales_price) {} 10 string isbn() const { return bookNo; } 11 virtual double net_price(size_t n) const { return n * price; } 12 virtual ~Quote() = default; 13 private: 14 string bookNo; 15 protected: 16 double price = 0.0; 17 }; 18 19 class Bulk_quote : public Quote { 20 public: 21 Bulk_quote() = default; 22 Bulk_quote(const string &book, double sales_price, size_t n, double dis): Quote(book, sales_price), min_qty(n), discount(dis) {} 23 double net_price(size_t n) const override; 24 private: 25 size_t min_qty = 0; 26 double discount = 0.0; 27 }; 28 29 double Bulk_quote::net_price(size_t n) const 30 { 31 if (n >= min_qty) 32 return n * (1 - discount) * price; 33 else 34 return n * price; 35 } 36 37 double print_total(ostream &os, const Quote &item, size_t n) 38 { 39 double ret = item.net_price(n); 40 os << "ISBN: " << item.isbn() << " # sold: " << n << " total due: " << ret << endl; 41 return ret; 42 } 43 44 45 int main() 46 { 47 Quote book("3-298-15-2", 37.0); 48 Bulk_quote book2("3-298-15-2", 37.0, 5, 0.2); 49 print_total(cout, book2, 1); 50 print_total(cout, book2, 5); 51 print_total(cout, book, 5); 52 return 0; 53 }
15.7
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Limited_quote : public Quote { 2 public: 3 double net_price(size_t cnt) const override; 4 private: 5 size_t max_qty = 0; 6 double discount = 0.0; 7 }; 8 9 double Limited_quote::net_price(size_t cnt) const 10 { 11 if (cnt <= max_qty) 12 return cnt * (1- discount) * price; 13 else 14 return max_qty * (1-discount) * price + (cnt - max_qty) * price; 15 }
15.8
知识点:存在继承关系的类型、(变量或)表达式的静态类型
静态类型:表达式的静态类型在编译时总是已知的,它是变量声明时的类型或表达式生成的类型
动态类型:动态类型直到运行时才可知,(变量或)表达式表示的内存中的对象的类型
如当print_total调用net_price时:double ret = item.net_price(n);,我们知道item的静态类型是Quote&,它的动态类型则依赖于item绑定的实参(即动态类型知道运行时调用print_total才知道),若我们传递一个Bulk_quote对象给print_total,则item的静态类型将与它的动态类型不一致(此时item的静态类型是Quote&,而相应的动态类型是Bulk_quote)
15.9
基类的引用(或指针)的静态类型可能与其动态类型不一致
Bulk_quote book;
Quote &item = book;
Quote *item1 = &book;
print_total(cout, book, 2);
15.10
ifstream in("data.txt");
read(istream &is, Sales_data &item) ====》 read(cin, total) ====》 read(in, total)
因为iostream是fstream的基类,且参数为基类的引用,故我们可以使用派生类的对象作为实参。
15.11
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 class Quote { 7 public: 8 Quote() = default; 9 Quote(const string &book, double sales_price): bookNo(book), price(sales_price) {} 10 string isbn() const { return bookNo; } 11 virtual void debug() const { cout << "bookNo\t" << "price\t"; } 12 virtual ~Quote() = default; 13 private: 14 string bookNo; 15 protected: 16 double price = 0.0; 17 }; 18 19 class Bulk_quote : public Quote { 20 public: 21 Bulk_quote() = default; 22 Bulk_quote(const string &book, double sales_price, size_t n, double dis): Quote(book, sales_price), min_qty(n), discount(dis) {} 23 void debug() const override { Quote::debug(); cout << "min_qty\t" << "discount\t"; } 24 private: 25 size_t min_qty = 0; 26 double discount = 0.0; 27 }; 28 29 int main() 30 { 31 Quote book("3-298-15-2", 37.0); 32 Bulk_quote book2("3-298-15-2", 37.0, 5, 0.2); 33 book.debug(); 34 cout << endl; 35 book2.debug(); 36 return 0; 37 }
15.12
有必要,因为它们两个的意思不包含也不互斥(两不相干),加上它们可以使成员函数的意义更明确。
15.13
基类base中的print函数:打印数据成员basename的值
派生类derived中的print函数:函数体中的print(os);本意是调用基类base的print函数,但在运行时该调用被解析为对自身的调用,从而导致无限递归
解决方法是:强迫其执行虚函数的某个特定版本,此题中我们强制其调用基类中的print函数(base::print(os);)
15.14
(a):调用基类版本的print()函数
(b):调用派生类版本的print()函数
(c):调用基类的name()函数
(d):调用派生类中基类部分的name()函数
(e):调用基类版本
(f):调用派生类版本
15.15
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Quote { 2 public: 3 Quote() = default; 4 Quote(const string &book, double sales_price): bookNo(book), price(sales_price) {} 5 string isbn() const { return bookNo; } 6 virtual double net_price(size_t) const; 7 virtual ~Quote() = default; 8 private: 9 string bookNo; 10 protected: 11 double price = 0.0; 12 }; 13 14 //抽象基类 15 class Disc_quote : public Quote { 16 public: 17 Disc_quote() = default; 18 Disc_quote(const string &book, double price, size_t qty, double disc): Quote(book, price), quantity(qty), discount(disc) { } 19 double net_price(size_t) const = 0; //纯虚函数 20 protected: 21 size_t quantity = 0; 22 double discount = 0.0; 23 }; 24 25 class Bulk_quote : public Disc_quote { 26 public: 27 Bulk_quote() = default; 28 Bulk_quote(const string &book, double price, size_t qty, double disc): Disc_quote(book, price, qty, disc) { } 29 double net_price(size_t) const override; 30 };
15.16
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Limited_quote : public Disc_quote { 2 public: 3 Limited_quote() = default; 4 Limited_quote(const string &book, double price, size_t qty, double disc, size_t mqty): Disc_quote(book, price, qty, disc), max_qty(mqty) { } 5 double net_price(size_t cnt) const override; 6 private: 7 size_t max_qty = 0; //超过max_qty本的部分为原价出售 8 };
15.17
错误信息:
[Error] cannot declare variable 'book' to be of abstract type 'Disc_quote'
[Note] because the following virtual functions are pure within 'Disc_quote':
[Note] virtual double Disc_quote::net_price(size_t) const
15.18
只有d1和dd1才能够赋值。
这是因为:只有当派生类公有地继承基类时,用户代码才能使用派生类向基类的转换;也就是说,如果派生类继承基类的方式是受保护的或者私有的,则用户代码不能使用该转换。
在题中,只有d1和dd1类是公有地继承基类,故只有它们才能完成向基类的转换。
15.19
我认为都不合法,因为b中的private成员我们在派生类中不可以访问。
不合法:Derived_from_private: public Priv_derv
纠正我的想法:基类的private成员是继承到派生类的,只是不能访问而已。
15.20
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Base { 2 public: 3 void pub_mem() 4 { 5 cout << "yes" << endl; 6 } 7 protected: 8 int prot_mem = 1; 9 private: 10 int priv_mem = 0; 11 }; 12 13 //公有继承 14 class Pub_derv : public Base { 15 void memfcn(Base &b){ b = *this; } 16 }; 17 18 //私有继承 19 class Priv_derv : private Base { 20 void memfcn(Base &b){ b = *this; } 21 }; 22 23 class Derived_from_public : public Pub_derv { 24 void memfcn(Base &b){ b = *this; } 25 }; 26 27 class Derived_from_private : public Priv_derv { 28 void memfcn(Base &b){ b = *this; } //报错 29 };
15.23
修改如下:
class D1 : public Base { public: int fcn() override; };
此时执行bp2->fcn();,发现在运行时调用D1::fcn
15.24
需要虚析构函数的类:基类通常应该定义一个虚析构函数,这样我们才能动态分配继承体系中的对象(特别是当一个动态分配的对象的指针指向继承体系中的某个类型,该指针的静态类型与被删除对象的动态类型不符时,如我们要删除一个指向派生类对象的基类指针时)
虚析构函数应该执行的操作:执行指针指向的类型的析构函数
15.25
因为如果Disc_quote没有定义默认构造函数,那么编译器将无法构造派生类(即Bulk_quote)对象的基类(即Disc_quote)部分。
去掉的话,将无法构造Disc_quote对象,因为其基类部分无法构造。
15.26
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 class Quote { 7 public: 8 Quote() = default; 9 Quote(const string &book, double sales_price): bookNo(book), price(sales_price) { cout << "基类带参构造函数\n"; } 10 Quote(const Quote &rhs): bookNo(rhs.bookNo), price(rhs.price) { cout << "基类拷贝构造函数\n"; } 11 Quote(Quote &&rhs): bookNo(move(rhs.bookNo)), price(move(rhs.price)) { cout << "基类移动构造函数\n"; } 12 Quote &operator=(const Quote &rhs) 13 { 14 bookNo = rhs.bookNo; 15 price = rhs.price; 16 cout << "基类拷贝赋值运算符\n"; 17 return *this; 18 } 19 Quote &operator=(Quote &&rhs) 20 { 21 bookNo = move(rhs.bookNo); 22 price = move(rhs.price); 23 cout << "基类移动赋值运算符\n"; 24 return *this; 25 } 26 string isbn() const { return bookNo; } 27 private: 28 string bookNo; 29 protected: 30 double price = 0.0; 31 }; 32 33 class Bulk_quote : public Quote { 34 public: 35 Bulk_quote() = default; 36 Bulk_quote(const string &book, double sales_price, size_t n, double dis): Quote(book, sales_price), min_qty(n), discount(dis) { cout << "派生类带参构造函数\n"; } 37 Bulk_quote(const Bulk_quote &rhs): Quote(rhs), min_qty(rhs.min_qty), discount(rhs.discount) { cout << "派生类拷贝构造函数\n"; } 38 Bulk_quote(Bulk_quote &&rhs): Quote(move(rhs)), min_qty(move(rhs.min_qty)), discount(move(rhs.discount)) { cout << "派生类移动构造函数\n"; } 39 Bulk_quote &operator=(const Bulk_quote &rhs) 40 { 41 Quote::operator=(rhs); 42 min_qty = rhs.min_qty; 43 discount = rhs.discount; 44 cout << "派生类拷贝赋值运算符\n"; 45 return *this; 46 } 47 Bulk_quote &operator=(Bulk_quote &&rhs) 48 { 49 Quote::operator=(move(rhs)); 50 min_qty = move(rhs.min_qty); 51 discount = move(rhs.discount); 52 cout << "派生类移动赋值运算符\n"; 53 return *this; 54 } 55 private: 56 size_t min_qty = 0; 57 double discount = 0.0; 58 }; 59 60 int main() 61 { 62 Quote q; //基类默认构造函数 63 Quote q1("2-318-19-5", 20.0); //基类带参构造函数 64 Quote q2(q1); //基类拷贝构造函数 65 Quote q3(move(q1)); //基类移动构造函数 66 q = q1; //基类拷贝赋值运算符 67 q = move(q1); //基类移动赋值运算符 68 Bulk_quote b; //基类默认构造函数+派生类默认构造函数 69 Bulk_quote b1("2-318-19-6", 30.0, 5, 0.2); //基类带参构造函数+派生类带参构造函数 70 Bulk_quote b2(b1); //基类拷贝构造函数+派生类拷贝构造函数 71 Bulk_quote b3(move(b1)); //基类移动构造函数+派生类移动构造函数 72 b = b1; //基类拷贝赋值运算符+派生类拷贝赋值运算符 73 b = move(b1); //基类移动赋值运算符+派生类移动赋值运算符 74 return 0; 75 }
15.27
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 class Quote { 7 public: 8 Quote() = default; 9 Quote(const string &book, double sales_price): bookNo(book), price(sales_price) { cout << "基类带参构造函数\n"; } 10 string isbn() const { return bookNo; } 11 private: 12 string bookNo; 13 protected: 14 double price = 0.0; 15 }; 16 17 class Bulk_quote : public Quote { 18 public: 19 Bulk_quote() = default; 20 using Quote::Quote; //不可继承默认、移动、拷贝构造函数 21 Bulk_quote(const string &book, double sales_price, size_t n, double dis): Quote(book, sales_price), min_qty(n), discount(dis) { cout << "派生类带参构造函数\n"; } 22 private: 23 size_t min_qty = 0; 24 double discount = 0.0; 25 }; 26 27 int main() 28 { 29 Bulk_quote b1("2-318-19-6", 30.0); //基类带参构造函数 30 return 0; 31 }
15.28
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 int main() 2 { 3 double sum = 0.0; 4 vector<Quote> basket; 5 Bulk_quote b("0-211-38-5", 37, 5, 0.2); 6 Bulk_quote b1("0-211-38-6", 44, 5, 0.2); 7 basket.push_back(b); 8 basket.push_back(b1); 9 for (auto &i : basket) 10 sum += i.net_price(10); 11 return 0; 12 }
15.29
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 int main() 2 { 3 double sum = 0.0; 4 vector<shared_ptr<Quote>> basket; 5 basket.push_back(make_shared<Bulk_quote>("0-211-38-5", 37, 5, 0.2)); 6 basket.push_back(make_shared<Bulk_quote>("0-211-38-6", 44, 5, 0.2)); 7 for (auto x : basket) 8 sum += x->net_price(10); 9 cout << sum << endl; 10 return 0; 11 }
程序产生的结果会存在差异。
因为当通过Quote类型的对象调用虚函数net_price时,不实行动态绑定,调用的是Quote类中定义的版本;而通过Quote类型的指针调用虚函数net_price,实行动态绑定,而该指针实际指向Bulk_quote类中定义的版本。
15.30
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <fstream> 3 #include <sstream> 4 #include <iterator> 5 #include <initializer_list> 6 #include <vector> 7 #include <string> 8 #include <cstring> 9 #include <deque> 10 #include <list> 11 #include <forward_list> 12 #include <array> 13 #include <stack> 14 #include <queue> 15 #include <algorithm> 16 #include <functional> 17 #include <map> 18 #include <set> 19 #include <cctype> 20 #include <unordered_map> 21 #include <unordered_set> 22 #include <memory> 23 #include <new> 24 #include <utility> 25 26 using namespace std; 27 using namespace std::placeholders; 28 29 class Quote { 30 public: 31 Quote() = default; 32 Quote(const string &book, double sales_price): bookNo(book), price(sales_price) { } 33 string isbn() const { return bookNo; } 34 virtual double net_price(size_t n) const { return n * price; } 35 virtual Quote* clone() const & { return new Quote(*this); } 36 virtual Quote* clone() && { return new Quote(move(*this)); } 37 private: 38 string bookNo; 39 protected: 40 double price = 0.0; 41 }; 42 43 class Bulk_quote : public Quote { 44 public: 45 Bulk_quote() = default; 46 Bulk_quote(const string &book, double sales_price, size_t n, double dis): Quote(book, sales_price), min_qty(n), discount(dis) { cout << "派生类带参构造函数\n"; } 47 double net_price(size_t n) const override; 48 Bulk_quote* clone() const & { return new Bulk_quote(*this); } 49 Bulk_quote* clone() && { return new Bulk_quote(move(*this)); } 50 private: 51 size_t min_qty = 0; 52 double discount = 0.0; 53 }; 54 55 double Bulk_quote::net_price(size_t n) const 56 { 57 if (n >= min_qty) 58 return n * (1 - discount) * price; 59 else 60 return n * price; 61 } 62 63 double print_total(ostream &os, const Quote &item, size_t n) 64 { 65 double ret = item.net_price(n); 66 os << "ISBN: " << item.isbn() << " # sold: " << n << " total due: " << ret << endl; 67 return ret; 68 } 69 70 class Basket { 71 public: 72 void add_item(const Quote &sale){ items.insert(shared_ptr<Quote>(sale.clone())); } 73 void add_item(Quote &&sale){ items.insert(shared_ptr<Quote>(move(sale).clone())); } 74 double total_receipt(ostream&) const; 75 private: 76 static bool compare(const shared_ptr<Quote> &lhs, const shared_ptr<Quote> &rhs){ return lhs->isbn() < rhs -> isbn(); } 77 multiset<shared_ptr<Quote>, decltype(compare)*> items{compare}; 78 }; 79 80 double Basket::total_receipt(ostream &os) const 81 { 82 double sum = 0.0; 83 for (auto iter = items.cbegin(); iter != items.cend(); iter = items.upper_bound(*iter)) { 84 sum += print_total(os, **iter, items.count(*iter)); 85 } 86 os << "Total Sale: " << sum << endl; 87 return sum; 88 } 89 90 int main() 91 { 92 Basket bt; 93 Quote q("legend", 25.0); 94 Bulk_quote b("league", 37.0, 2, 0.2), b1 = b, b2 = b; 95 bt.add_item(q); 96 bt.add_item(b); 97 bt.add_item(b1); 98 bt.add_item(b2); 99 bt.total_receipt(cout); 100 return 0; 101 }
15.31