15.1 什么是虚成员?
对于某些函数,基类希望它的派生类各自定义适合自身的版本,此时基类就将这些函数声明成虚函数。
15.2 protected 访问说明符与 private 有何区别?
protected : 基类和和其派生类还有友元可以访问。
private : 只有基类本身和友元可以访问。
15.3 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <string> class Quote {public : Quote () = default ; Quote (const std::string &book,double sales_price):bookNo (book),price (sales_price){} std::string isbn () const { return bookNo; } virtual double net_price (std::size_t n) const { return n * price; } virtual ~Quote () = default ; virtual void debug () const ; private : std::string bookNo; protected : double price = 0.0 ; };
1 2 3 4 5 6 7 8 #include "Quote.h" double print_total (std::ostream& os, const Quote& item, std::size_t n) { double ret = item.net_price (n); os << "ISBN: " << item.isbn () << " # sold: " << n << " total due: " << ret << std::endl; return ret; }
15.5 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Bulk_quote :public Quote {public : Bulk_quote () = default ; Bulk_quote (const std::string& book, double sales_price,std::size_t qty,double disc) :Quote (book,sales_price),min_qty (qty),discount (disc){} double net_price (std::size_t ) const override ; void debug () const override ; private : std::size_t min_qty = 0 ; double discount = 0.0 ; }; double Bulk_quote::net_price (std::size_t cnt) const { if (cnt >= min_qty) return cnt * (1 - discount) * price; else return cnt * price; }
15.6 1 2 3 4 5 6 7 8 9 10 11 12 13 int main () { Quote q ("123" , 10 ) ; Bulk_quote bq ("123" , 10 , 5 , 0.2 ) ; auto t1 = print_total (std::cout, q, 4 ); auto t2 = print_total (std::cout, bq, 10 ); return 0 ; }
15.7 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class Limit_quote :public Quote {public : Limit_quote () = default ; Limit_quote (const std::string& book, double sales_price, std::size_t max, double disc) :Quote (book,sales_price),max_qty (max),discount (disc) {} double net_price (std::size_t ) const override ; void debug () const override ; private : std::size_t max_qty = 0 ; double discount = 0.0 ; }; double Limit_quote::net_price (std::size_t cnt) const { if (cnt <= max_qty) return cnt * (1 - discount) * price; else return max_qty * (1 - discount) * price + (cnt - max_qty) * price; } int main () { Limit_quote lq ("159" , 10 , 5 , 0.2 ) ; print_total (std::cout, lq, 4 ); print_total (std::cout, lq, 10 ); return 0 ; }
15.11 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void Quote::debug () const { std::cout << "This is Quote Class" << std::endl; std::cout << "ISBN: " << bookNo << std::endl; std::cout << "Price: " << price << std::endl; } void Bulk_quote::debug () const { std::cout << "This is Bulk_quote Class" << std::endl; std::cout << "min_qty: " << min_qty << std::endl; std::cout << "discount: " << discount << std::endl; std::cout << "Price: " << price << std::endl; } void Limit_quote::debug () const { std::cout << "This is Bulk_quote Class" << std::endl; std::cout << "max_qty: " << max_qty << std::endl; std::cout << "discount: " << discount << std::endl; std::cout << "Price: " << price << std::endl; }
15.12
有必要将一个成员函数同时声明成 override 和 final 吗?为什么?
有必要。override 的含义是重写基类中相同名称的虚函数,final 是阻止它的派生类重写当前虚函数。
15.15-15.16 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 class Quote { friend bool operator !=(const Quote& lhs, const Quote& rhs); public : Quote () { std::cout << "default constructing Quote\n" ; } Quote (const std::string& b, double p) : bookNo (b), price (p) { std::cout << "Quote : constructor taking 2 parameters\n" ; } Quote (const Quote& q) : bookNo (q.bookNo), price (q.price) { std::cout << "Quote: copy constructing\n" ; } Quote (Quote&& q) noexcept : bookNo (std::move (q.bookNo)), price (std::move (q.price)) { std::cout << "Quote: move constructing\n" ; } Quote& operator =(const Quote& rhs) { if (*this != rhs) { bookNo = rhs.bookNo; price = rhs.price; } std::cout << "Quote: copy =() \n" ; return *this ; } Quote& operator =(Quote&& rhs) noexcept { if (*this != rhs) { bookNo = std::move (rhs.bookNo); price = std::move (rhs.price); } std::cout << "Quote: move =!!!!!!!!! \n" ; return *this ; } std::string isbn () const { return bookNo; } virtual double net_price (std::size_t n) const { return n * price; } virtual void debug () const ; virtual ~Quote () { std::cout << "destructing Quote\n" ; } private : std::string bookNo; protected : double price = 10.0 ; }; bool inline operator !=(const Quote& lhs, const Quote& rhs){ return lhs.bookNo != rhs.bookNo && lhs.price != rhs.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) {} double net_price (std::size_t ) const = 0 ; protected : std::size_t quantity = 0 ; double discount = 0.0 ; }; class Bulk_quote : public Disc_quote{ public : Bulk_quote () { std::cout << "default constructing Bulk_quote\n" ; } using Disc_quote::Disc_quote; Bulk_quote (const Bulk_quote& bq) : Disc_quote (bq) { std::cout << "Bulk_quote : copy constructor\n" ; } Bulk_quote (Bulk_quote&& bq) noexcept : Disc_quote (std::move (bq)) { std::cout << "Bulk_quote : move constructor\n" ; } Bulk_quote& operator =(const Bulk_quote& rhs) { Disc_quote::operator =(rhs); std::cout << "Bulk_quote : copy =()\n" ; return *this ; } Bulk_quote& operator =(Bulk_quote&& rhs) noexcept { Disc_quote::operator =(std::move (rhs)); std::cout << "Bulk_quote : move =()\n" ; return *this ; } double net_price (std::size_t n) const override ; void debug () const override ; ~Bulk_quote () override { std::cout << "destructing Bulk_quote\n" ; } }; class Limit_quote :public Disc_quote {public : Limit_quote () = default ; Limit_quote (const std::string& book, double sales_price, std::size_t qty, double disc) :Disc_quote (book, sales_price, qty, disc) {} double net_price (std::size_t ) const override ; void debug () const override ; }; double Bulk_quote::net_price (std::size_t cnt) const { if (cnt >= quantity) return cnt * (1 - discount) * price; else return cnt * price; } double Limit_quote::net_price (std::size_t cnt) const { if (cnt <= quantity) return cnt * (1 - discount) * price; else return quantity * (1 - discount) * price + (cnt - quantity) * price; } void Quote::debug () const { std::cout << "This is Quote Class" << std::endl; std::cout << "ISBN: " << bookNo << std::endl; std::cout << "Price: " << price << std::endl; } void Bulk_quote::debug () const { std::cout << "This is Bulk_quote Class" << std::endl; std::cout << "min_qty: " << quantity << std::endl; std::cout << "discount: " << discount << std::endl; std::cout << "Price: " << price << std::endl; } void Limit_quote::debug () const { std::cout << "This is Bulk_quote Class" << std::endl; std::cout << "max_qty: " << quantity << std::endl; std::cout << "discount: " << discount << std::endl; std::cout << "Price: " << price << std::endl; }
15.17
15.23 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 class Base { public : void pub_mem () ; protected : int prot_mem; private : char priv_mem; }; struct Pub_Derv : public Base{ void memfcn (Base& b) { b = *this ; } }; struct Priv_Derv : private Base{ void memfcn (Base& b) { b = *this ; } }; struct Prot_Derv : protected Base{ void memfcn (Base& b) { b = *this ; } }; struct Derived_from_Public : public Pub_Derv{ void memfcn (Base& b) { b = *this ; } }; struct Derived_from_Private : public Priv_Derv{ }; struct Derived_from_Protected : public Prot_Derv{ void memfcn (Base& b) { b = *this ; } }; int main () { Pub_Derv d1; Base* p = &d1; Priv_Derv d2; Prot_Derv d3; Derived_from_Public dd1; p = &dd1; Derived_from_Private dd2; Derived_from_Protected dd3; return 0 ; }
12.28 1 2 3 4 5 vector<Quote> vec; vec.push_back (Bulk_quote ("!23" , 10 , 5 , 0.2 )); cout << vec.back ().net_price (10 ) << endl;
15.29 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int main () { std::vector<shared_ptr<Quote>> basket; basket.push_back (make_shared <Quote>("hello" , 10 )); basket.push_back (make_shared <Bulk_quote>("world" , 20 , 5 , 0.2 )); basket.push_back (make_shared <Limit_quote>("good" , 15 , 5 , 0.2 )); double all = 0.0 ; for (auto const & i : basket) { cout << i->net_price (10 ) << endl; all += i->net_price (10 ); } cout << all << endl; return 0 ; }
15.30 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 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>(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 basket; for (unsigned i = 0 ; i != 10 ; ++i) basket.add_item (Bulk_quote ("Bible" , 20.6 , 20 , 0.3 )); for (unsigned i = 0 ; i != 10 ; ++i) basket.add_item (Bulk_quote ("C++Primer" , 30.9 , 5 , 0.4 )); for (unsigned i = 0 ; i != 10 ; ++i) basket.add_item (Quote ("CLRS" , 40.1 )); ofstream log ("log.txt" , std::ios_base::app | std::ios_base::out) ; cout << basket.total_receipt (log) << endl; return 0 ; }
15.32
当一个 Query 类型的对象被拷贝、移动、赋值或销毁时,将分别发生什么?
拷贝: 当被拷贝时,合成的拷贝构造函数被调用。它将拷贝两个数据成员至新的对象。而在这种情况下,数据成员是一个智能指针,当拷贝时,相应的智能指针指向相同的地址,计数器增加1.
移动: 当移动时,合成的移动构造函数被调用。它将移动数据成员至新的对象。这时新对象的智能指针将会指向原对象的地址,而原对象的智能指针为 nullptr,新对象的智能指针的引用计数为 1.
赋值: 合成的赋值运算符被调用,结果和拷贝的相同的。
销毁: 合成的析构函数被调用。对象的智能指针的引用计数递减,当引用计数为 0 时,对象被销毁。
15.36 1 2 3 4 5 6 7 8 9 10 11 12 13 14 Query q = Query ("fiery" ) & Query ("bird" ) | Query ("wind" ); 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)
1 2 3 4 5 6 7 8 9 10 11 12 13 std::cout << q <<std::endl; Query::rep () BinaryQuery::rep () Query::rep () WodQuery::rep () Query::rep () BinaryQuery::rep () Query::rep () WodQuery::rep () Query::rep () WodQuery::rep () ((fiery & bird) | wind)
Query 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 #include <iostream> using namespace std;#include <vector> #include <algorithm> #include <map> #include <set> #include <memory> #include <string> #include <fstream> #include <sstream> class QueryResult ;class TextQuery {public : friend QueryResult; using line_no = vector<string>::size_type; TextQuery (ifstream&); QueryResult query (const string&) const ; private : shared_ptr<vector<string>> file; map<string, shared_ptr<set<line_no>>>wm; }; class QueryResult { friend ostream& print (ostream&, const QueryResult&) ; public : QueryResult (string s, shared_ptr<set<TextQuery::line_no>>p, shared_ptr<vector<string>> f) :sought (s), lines (p), file (f) {} using line_no = TextQuery::line_no; using Iter = std::set<line_no>::iterator; Iter begin () const { return lines->begin (); } Iter end () const { return lines->end (); } shared_ptr<std::vector<std::string>> get_file () const { return std::make_shared<std::vector<std::string>>(file); } private : string sought; shared_ptr<set<TextQuery::line_no>>lines; shared_ptr<vector<string>> file; }; class Query ;class Query_base { friend class Query ; protected : using line_no = TextQuery::line_no; virtual ~Query_base () = default ; private : virtual QueryResult eval (const TextQuery&) const = 0 ; 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&); QueryResult eval (const TextQuery& t) const { return q->eval (t); } string rep () const { std::cout << "Query::rep() \n" ; return q->rep (); } private : Query (shared_ptr<Query_base>query):q (query){ std::cout << "Query::Query(std::shared_ptr<Query_base> query)\n" ; } shared_ptr<Query_base>q; }; class WordQuery :public Query_base { friend class Query ; WordQuery (const string& s) :query_word (s){} QueryResult eval (const TextQuery& t) const {return t.query (query_word);} string rep () const {return query_word;} string query_word; }; class NotQuery :public Query_base { friend Query operator ~(const Query&); NotQuery (const Query&q):query (q) {} string rep () const { return "~(" + query.rep () + ")" ; } QueryResult eval (const TextQuery&) const ; Query query; }; class BinaryQuery :public Query_base {protected : BinaryQuery (const Query&l,const Query&r,string s):lhs (l),rhs (r),opSym (s){} string rep () const { return "(" + lhs.rep () + " " + opSym + " " + rhs.rep () + ")" ; } Query lhs, rhs; string opSym; }; class AndQuery :public BinaryQuery { friend Query operator &(const Query&, const Query&); AndQuery (const Query&left,const Query& right):BinaryQuery (left,right,"&" ) {} QueryResult eval (const TextQuery&) const ; }; class OrQuery :public BinaryQuery { friend Query operator |(const Query&, const Query&); OrQuery (const Query& left, const Query& right) :BinaryQuery (left, right, "|" ) {} QueryResult eval (const TextQuery&) const ; };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 #include "TextQuery.h" TextQuery::TextQuery (ifstream& is) :file (new vector<string>) { string text; while (getline (is, text)) { file->push_back (text); int n = file->size () - 1 ; istringstream line (text) ; string word; while (line >> word) { auto & lines = wm[word]; if (!lines) lines.reset (new set<line_no>); lines->insert (n); } } } QueryResult TextQuery::query (const string& sought) const { static shared_ptr<set<line_no>> nodata (new set<line_no >); auto loc = wm.find (sought); if (loc == wm.end ()) return QueryResult (sought, nodata, file); else return QueryResult (sought, loc->second, file); } ostream& print (ostream& os, const QueryResult& qr) { os << qr.sought << " occurs " << qr.lines->size () << " " << endl; for (auto num : *qr.lines) { os << "\t(line " << num + 1 << ")" << *(qr.file->begin () + num) << endl; } return os; } void runQueries (ifstream& infile) { TextQuery tq (infile) ; while (true ) { cout << "enter word to look for,or q to quit: " ; string s; if (!(cin >> s) || s == "q" ) break ; print (cout, tq.query (s)) << endl; } } inline Query::Query (const string&s) :q(new WordQuery(s)) { std::cout << "Query::Query(const std::string& s) where s=" + s + "\n" ; } Query operator ~(const Query& operand) { return shared_ptr <Query_base>(new NotQuery (operand)); } Query operator &(const Query&lhs, const Query&rhs) { return shared_ptr <Query_base>(new AndQuery (lhs, rhs)); } Query operator |(const Query&lhs, const Query&rhs) { return shared_ptr <Query_base>(new OrQuery (lhs, rhs)); } inline std::ostream&operator << (std::ostream& os, const Query& query){ return os << query.rep (); } QueryResult OrQuery::eval (const TextQuery& text) const { auto right = rhs.eval (text), left = lhs.eval (text); auto 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 ()); } QueryResult AndQuery::eval (const TextQuery&text) const { auto right = rhs.eval (text), left = lhs.eval (text); auto ret_lines = make_shared<set<line_no>>(); 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 n = 0 ; n != sz; ++n) { if (beg == end || *beg != n) { ret_lines->insert (n); } else if (beg != end) ++beg; } return QueryResult (rep (), ret_lines, result.get_file ()); }