c++primer 5th第15章基础、课后习题自己解析、心得体会等
先看一個例子:
#include <string> #include <memory> #include <iostream> using namespace std; class A{private:int * d;size_t * use_count;public:A(int n):d(new int(n)),use_count(new size_t(1)){}A(const A & a):d(a.d),use_count(a.use_count){++ * use_count;}A& operator=(const A& a){++ *(a.use_count);if(-- *use_count == 0){delete use_count;delete d; }use_count = a.use_count;d = a.d; return *this;}~A(){if(--*use_count == 0){delete use_count;delete d;}use_count = nullptr;d = nullptr;}void ret_use(){cout << *use_count << endl;}};int main() { A a(1234); a.ret_use(); A b(a); a.ret_use(); b.ret_use(); A *c = new A(a); c->ret_use(); a.ret_use(); b.ret_use();return 0; }運行結果:?
1 2 2 3 3 3結論:new 動態分配對象和普通對象調用的拷貝構造函數成員或者構造函數完全一致。不過就是存儲的方式不同罷了。創建對象所調用的那些函數(構造函數是一樣樣的)。?
delete 一個指類類型的動態類型指針會發生什么??
class Quote{ public:std::string isbn() const;virtual double new_price(size_t n) const; }; class Bulk_quote : public Quote { public:double net_price(size_t )const override; };虛函數:對于基類的某些函數,基類希望它的派生類能各自定義適合自己的版本,此時基類就將這些函數聲明成虛函數(virtual function)。
訪問說明符:比如private,public,protected;
override:c++11允許派生類顯式地注明它使用哪個成員函數改寫基類的虛函數,具體措施是在該類的形參列表之后增加一個override關鍵字。override位于&,const,等之后。
動態綁定:當通過一個基類的指針或者引用調用一個虛函數時候,這個虛函數會根據調用的實際對象類型選擇使用對應的版本,這個過程叫做動態綁定。因為是在運行時選擇函數的版本,所以動態綁定又叫做運行時綁定。
訪問控制與繼承:繼承類的成員函數可以直接訪問基類的public成員和protected成員,但是不能直接訪問基類的private成員。
習題15.2.1練習
練習15.1
答:類中被聲明為virtual的成員,基類希望這種成員在派生類中重新定義。除了構造函數外,任意非static成員都可以定義為虛成員。
練習15.2
答:protected是受保護的成員,可以被該類的成員、友元和派生類成員(非友元)訪問,而不可以被該類的普通用戶訪問。而private成員只能被基類的友元和成員訪問,不能被派生類訪問。
練習15.3
答:
定義你自己的quote類和print_total成員函數。
15.4
b對,a,c錯誤。
15.5
答:
15.6
class Bulk_quote : public Quote //Bulk_quote derived from Quote { public:Bulk_quote() = default;Bulk_quote(const string &,double,size_t ,double);//覆蓋基類的版本以實現大量購買的折扣和政策double net_price(size_t n)const override; //override:顯式地用這個成員函數改寫基類的虛函數} private:size_t min_qty;double discount = 0.0;};15.7
class Limitted_quote: public Quote {public:double net_price(size_t cnt)const override{if(cnt <= min_qty){return cnt * (1 - discount) * price; }elsereturn min_qty * (1-discount)*price + (cnt - min_qty) * price; }private:size_t min_qty;double discount;};15.8
靜態類型:變量聲明的類型或者表達式生成的類型,靜態類型在編譯期就已經決定了。動態類型:變量或者表達式表示的內存中的對象的類型,動態類型直到運行時才能知道。如 Quote * pQuote =
new Bulk_quote;指針pQuote的靜態類型是Quote,而動態類型是Bulk_quote,直到運行時才能知道它指向的類型是基類還是派生類。如果一個變量非指針也非引用,則它的靜態類型和動態類型永遠一致,但基類的指針或者引用的動態類型可能與其靜態類型不一致。
?
15.9
當一個對象是非指針也非引用,靜態類型和動態類型永遠一致。當給基類對象的指針或者引用賦值或者初始化時,靜態類型和動態類型可能不一致。
Bulk_quote bulk; Quote * pQuote = & bulk; Quote & rQuote = bulk; 傳遞給item的如果是派生類對象,即是靜態類型和動態類型不同的情況。 double print_total(ostream &os,const Quote &item,size_t n);15.10
可能傳遞給iostream對象,也可以傳遞派生類對象,比如fstream對象。
15.3虛函數
習題15.11
void Quote::debug_1()const { cout << "bookNo:" << bookNo << endl; cout << "price:" << price << endl; }void Bulk_quote::debug_1()const { cout << "min_qty:" << min_qty << endl; cout << "discount:" << discount << endl; }15.12
有必要
override:c++11新標準中我們可以使用override關鍵字來說明派生類中的虛函數,這么做的好處是使得我們的意圖更加清洗即明確地告訴編譯器我們想要覆蓋已存在的虛函數。如果定義個了函數與基類中的名字相同但是形參列表不同,在不使用override關鍵字的時候這種函數定義是合法的,在使用了override關鍵字之后這種行為是非法的,編譯器會提示出錯。
? ? ? ? final:如果我們將某個函數定義成final,則不允許后續的派生類來覆蓋這個函數,否則會報錯。
? ? ? ? 因此,將一個成員函數聲明稱為override和final能夠使得我們的意圖更加清晰。
15.13
該派生類的虛函數想要調用它覆蓋的基類的虛函數版本,但是沒有使用使用作用域運算符,會導致無限遞歸。
fixed: void print(ostream &os) {base::print(os); os << "? " << i;}
15.15
#include <string> #include <iostream> using namespace std;class Bulk_quote; class Quote{ public:Quote() = default;Quote(const string &book,double sales_price):bookNo(book),price(sales_price){}string isbn()const;virtual double net_price(size_t n)const;double print_total(ostream &os,const Quote &item,size_t n);virtual ~Quote();virtual void debug_1()const; private:string bookNo; protected:double price = 0.0; };class Limitted_quote: public Quote {public:double net_price(size_t cnt)const override{if(cnt <= min_qty){return cnt * (1 - discount) * price; }elsereturn min_qty * (1-discount)*price + (cnt - min_qty) * price; }private:size_t min_qty;double discount;};class Disc_quote: public Quote{public:Disc_quote() = default;Disc_quote(const string &book,double price,size_t qty,double disc):Quote(book,price),quantity(qty),discount(disc){}double net_price(size_t)const = 0;private: size_t quantity = 0; double discount = 0;}; class Bulk_quote : public Disc_quote //Bulk_quote derived from Quote { public:Bulk_quote() = default;Bulk_quote(const string &,double,size_t ,double);//覆蓋基類的版本以實現大量購買的折扣和政策double net_price(size_t n)const override; //override:顯式地用這個成員函數改寫基類的虛函數void debug_1()const override;private:size_t min_qty = 0;double discount = 0.0;};15.17
編譯器會給出錯誤信息:Disc_quote is an abstract type......意思是Disc_quote是一個抽象基類。不能定義抽象基類的對象。
15.18
只有d1和dd1可以。因為只有派生類共有地繼承基類時,用戶代碼才能使用派生類到基類的轉換。只有d1和dd1是共有地繼承,所以只有這2個是可以的。
或者可以這樣理解,只有當基類的共有成員是可訪問的,才能使用派生類到基類的轉換。目前對于用戶來說,共有成員只對d1和dd1是可見的。
15.19
如果派生類繼承基類是共有的,那么所有用戶都可以使用從派生類到基類的轉換。
如果派生類繼承基類是共有的或者是受保護的,那么派生類的成員函數和友元可以使用從派生類到基類的轉換,普通用戶不可以。
如果D繼承B是共有的或者受保護的,那么D的派生類或的成員或友元可以使用從D到B的轉換,如果D到B的繼承是私有的,那么D的派生類的成員或者友元將不能使用D到B的轉換。
一戶話:如果基類的共有成員對某個用戶(對象用戶或者代碼實現用戶)是可見的,那么從派生類到基類是可以轉換的,反之則不可以。
Derived_from_private: private Priv_Derv這個類的函數不合法。
15.20
#include <string> #include <iostream> using namespace std; class Base{public:void pub_mem();protected:int prot_mem = -1234;private:char priv_mem;};struct Pub_Derv: public Base { int f(){return prot_mem;}void memfcn(Base &b){b = *this;cout << "Pub_Derv" << endl;} };struct Priv_Derv: private Base { int f1() const{return prot_mem;} void memfcn(Base &b) {b = *this;cout << "Priv_Derv" << endl; } };struct Prot_Derv:protected Base { int f2(){return prot_mem;} void memfcn(Base &b) { b = *this; cout << "Prot_Derv" << endl; } };struct Derived_from_Public:public Pub_Derv { int use_base(){return prot_mem;} void memfcn(Base &b){b= *this;cout << "Derived_from_Public" << endl; } };struct Derived_from_Protected:protected Prot_Derv { int use_base() {return prot_mem; } void memfcn(Base &b) {b = *this;cout << "Derived_from_Protected" << endl; } };int main() { Pub_Derv d1; Priv_Derv d2; Prot_Derv d3; Derived_from_Public dd1; //Derived_from_Private dd2; Derived_from_Protected dd3; Base base; Base *p = new Base; p = &d1; //p = &d2; //p = &d3; p = &dd1; //p = &dd2; //p = &dd3; d1.memfcn(base); d2.memfcn(base); d3.memfcn(base);dd1.memfcn(base); //dd2.memfcn(base); dd3.memfcn(base); return 0;return 0; }15.24
作為基類使用的類應該具有虛析構函數,以保證在刪除指向動態分配對象的基類指針時,根據基類的實際指向的對象所屬的類型運行時當的析構函數。
? ? ? ? 虛析構函數可以為空,即不執行任何操作。一般而言,析構函數的主要作用是清除本類中定義的數據成員。如果該類沒有定義指針類的成員,則使用合成版本即可;如果該類定義了指針成員,則一般需要自定義析構函數以保證對指針成員進行適當的清除。因此,如果有虛析構函數必須執行的操作,則就是清除本類中定義的數據成員的操作。
15.26
自己編寫版本(后面有書上的答案部分)
#include <string> #include <iostream> using namespace std; class Quote{public:Quote(){cout << "Quote()"<< endl;};Quote(const Quote & rhs){cout << "Quote(const Quote &rhs)" << endl;}Quote(Quote &&rhs){ cout << "Quote(Quote &&rhs)" << endl;}Quote &operator=(const Quote &rhs) {cout << "Quote& operator=(const Quote&)" << endl;bookNo = rhs.bookNo;price = rhs.price;return *this;}Quote &operator=(Quote &&rhs){bookNo = std::move(rhs.bookNo);price = std::move(rhs.price);cout <<"Quote& operator(Quote &&)" << endl;return *this;}virtual ~Quote() {cout << "~Quote()" << endl;}private:string bookNo; double price = 0.0; }; class Disc_quote : public Quote {public:Disc_quote(){ cout << "Disc_quote()" << endl;}Disc_quote(const Disc_quote &rhs):Quote(rhs),quantity(),discount(){cout << "Disc_quote(const Disc_quote&)" <<endl; }Disc_quote(Disc_quote && rhs):Quote(std::move(rhs)),quantity(rhs.quantity),discount(rhs.discount){cout << "Disc_quote(Disc_quote(Disc_quote &&rhs))" << endl;}Disc_quote& operator=(const Disc_quote & rhs){quantity = rhs.quantity;discount = rhs.discount;cout << "Disc_count&operator=(const Disc_quote &rhs) " << endl;return *this; }~Disc_quote()override {cout <<"~Disk_quote()"<< endl;}protected:size_t quantity = 0;double discount = 0;};class Bulk_quote : public Disc_quote {public:Bulk_quote(){cout << "Bulk_quote()" <<endl;}Bulk_quote(const Bulk_quote & rhs):Disc_quote(rhs){}Bulk_quote(Bulk_quote && rhs):Disc_quote(rhs){}Bulk_quote& operator=(const Bulk_quote &rhs){Disc_quote::operator=(rhs);cout << "operator=(const Bulk_quote) " << endl;return *this; }Bulk_quote& operator=(Bulk_quote && rhs){Disc_quote::operator=(std::move(rhs));cout << "Bulk_quote(Bulk_quote && rhs)" << endl; return *this;}~Bulk_quote() override {cout << "~Bulk_quote()" << endl; }};int main() {Bulk_quote b1;return 0; } Quote() Disc_quote() Bulk_quote() ~Bulk_quote() ~Disk_quote() ~Quote()書上的答案(15.26)Quote部分:
class Quote{public:Quote(const string &book = "",double sales_price = 0):bookNo(book),price(sales_price){cout << "call Quote(const string &book,double sales_price = 0)" << endl;}Quote(const Quote & rhs){cout << "Quote(const Quote &rhs)" << endl;}Quote(Quote &&rhs){ cout << "Quote(Quote &&rhs)" << endl;}Quote &operator=(const Quote &rhs) {cout << "Quote& operator=(const Quote&)" << endl;bookNo = rhs.bookNo;price = rhs.price;return *this;}Quote &operator=(Quote &&rhs){bookNo = std::move(rhs.bookNo);price = std::move(rhs.price);cout <<"Quote& operator(Quote &&)" << endl;return *this;}virtual double net_price(size_t n)const{return n * price; }virtual ~Quote() {cout << "~Quote()" << endl;}friend ostream & operator<<(ostream &os,const Quote & rhs){os << "bookNo:"<<rhs.bookNo << "price:" << rhs.price;return os;}private:string bookNo; double price = 0.0; };習題15.27?
#include <string> #include <iostream> using namespace std; class Quote{public:Quote(){cout << "Quote()"<< endl;};Quote(const Quote & rhs){cout << "Quote(const Quote &rhs)" << endl;}Quote(const string &book,double sales_price):bookNo(book),price(sales_price){}Quote(Quote &&rhs){ cout << "Quote(Quote &&rhs)" << endl;}Quote &operator=(const Quote &rhs) {cout << "Quote& operator=(const Quote&)" << endl;bookNo = rhs.bookNo;price = rhs.price;return *this;}Quote &operator=(Quote &&rhs){bookNo = std::move(rhs.bookNo);price = std::move(rhs.price);cout <<"Quote& operator(Quote &&)" << endl;return *this;}virtual ~Quote() {cout << "~Quote()" << endl;}protected:string bookNo; double price = 0.0; }; class Disc_quote : public Quote {public:using Quote::Quote;Disc_quote(){ cout << "Disc_quote()" << endl;}Disc_quote(const Disc_quote &rhs):Quote(rhs),quantity(),discount(){cout << "Disc_quote(const Disc_quote&)" <<endl; }Disc_quote(Disc_quote && rhs):Quote(std::move(rhs)),quantity(rhs.quantity),discount(rhs.discount){cout << "Disc_quote(Disc_quote(Disc_quote &&rhs))" << endl;}Disc_quote& operator=(const Disc_quote & rhs){quantity = rhs.quantity;discount = rhs.discount;cout << "Disc_count&operator=(const Disc_quote &rhs) " << endl;return *this; }~Disc_quote()override {cout <<"~Disk_quote()"<< endl;}friend ostream &operator<<(ostream &os,const Disc_quote & q){os << "bookNo:" << q.bookNo << " " << "price:" << q.price << " quantity:" << q.quantity << " discount:" << q.discount << endl;return os;}protected:size_t quantity = 0;double discount = 0;};class Bulk_quote : public Disc_quote {public:Bulk_quote(){cout << "Bulk_quote()" <<endl;}Bulk_quote(const Bulk_quote & rhs):Disc_quote(rhs){}Bulk_quote(Bulk_quote && rhs):Disc_quote(rhs){}Bulk_quote& operator=(const Bulk_quote &rhs){Disc_quote::operator=(rhs);cout << "operator=(const Bulk_quote) " << endl;return *this; }Bulk_quote& operator=(Bulk_quote && rhs){Disc_quote::operator=(std::move(rhs));cout << "Bulk_quote(Bulk_quote && rhs)" << endl; return *this;}~Bulk_quote() override {cout << "~Bulk_quote()" << endl; }};int main() {Bulk_quote b1; Disc_quote d("isbn-123-345-456",12.12); cout << d << endl;return 0; }?15.28
#include <string> #include <iostream> #include <vector> #include <memory> using namespace std; class Quote{public:Quote(){cout << "Quote()"<< endl;};Quote(const Quote & rhs){cout << "Quote(const Quote &rhs)" << endl;}Quote(const string &book,double sales_price):bookNo(book),price(sales_price){}Quote(Quote &&rhs){ cout << "Quote(Quote &&rhs)" << endl;}Quote &operator=(const Quote &rhs) {cout << "Quote& operator=(const Quote&)" << endl;bookNo = rhs.bookNo;price = rhs.price;return *this;}Quote &operator=(Quote &&rhs){bookNo = std::move(rhs.bookNo);price = std::move(rhs.price);cout <<"Quote& operator(Quote &&)" << endl;return *this;}virtual double net_price(size_t n)const{cout << "Quote::net_price(size_t)" << endl;return n * price;}virtual ~Quote() {cout << "~Quote()" << endl;}protected:string bookNo; double price = 0.0; }; class Disc_quote : public Quote {public:using Quote::Quote;Disc_quote(){ cout << "Disc_quote()" << endl;}Disc_quote(const Disc_quote &rhs):Quote(rhs),quantity(),discount(){cout << "Disc_quote(const Disc_quote&)" <<endl; }Disc_quote(const string &book,double sales_price,size_t qty,double disc):Quote(book,sales_price),quantity(qty),discount(disc){cout << "Disc_quote(const string&,double,size_t ,double)" << endl;}Disc_quote(Disc_quote && rhs):Quote(std::move(rhs)),quantity(rhs.quantity),discount(rhs.discount){cout << "Disc_quote(Disc_quote(Disc_quote &&rhs))" << endl;}Disc_quote& operator=(const Disc_quote & rhs){quantity = rhs.quantity;discount = rhs.discount;cout << "Disc_count&operator=(const Disc_quote &rhs) " << endl;return *this; }~Disc_quote()override {cout <<"~Disk_quote()"<< endl;}double net_price(size_t n)const override{cout << "Disc_quote::net_price(size_t)" << endl;return n * price;}friend ostream &operator<<(ostream &os,const Disc_quote & q){os << "bookNo:" << q.bookNo << " " << "price:" << q.price << " quantity:" << q.quantity << " discount:" << q.discount << endl;return os;}protected:size_t quantity = 0;double discount = 0;};class Bulk_quote : public Disc_quote {public:Bulk_quote(){cout << "Bulk_quote()" <<endl;}Bulk_quote(const Bulk_quote & rhs):Disc_quote(rhs){}Bulk_quote(Bulk_quote && rhs):Disc_quote(rhs){}Bulk_quote& operator=(const Bulk_quote &rhs){Disc_quote::operator=(rhs);cout << "operator=(const Bulk_quote) " << endl;return *this; }Bulk_quote& operator=(Bulk_quote && rhs){Disc_quote::operator=(std::move(rhs));cout << "Bulk_quote(Bulk_quote && rhs)" << endl; return *this;}~Bulk_quote() override {cout << "~Bulk_quote()" << endl; }};int main() { /* //根據打印結果,如果vector類型為vector<Quote>,那么存儲的類型都是Quote類型,即使賦值時候的類型是 //Disc_quote類型,賦值的過程中也會把繼承部分切掉。//自己編寫的例子 vector<Quote> v1; v1.push_back(Quote("isbn-001-v1",12.4)); v1.push_back(Disc_quote("isbn-002-v1",12.6,100,14.2)); cout << v1[0].net_price(10) << endl; cout << v1[1].net_price(20) << endl;//自己編寫的例子:v2是Quote*類型 vector<shared_ptr<Quote>> v2; v2.push_back(make_shared<Quote>(Quote("isbn1-net1-0001",12.5))); v2.push_back(make_shared<Disc_quote>(Disc_quote("isbn2-net2-0002",12.5,100,14))); cout << v2[0]->net_price(10) << endl; cout << v2[1]->net_price(10) << endl; *///結果發現,定義時候的類型是Quote,即使插入的元素類型是Disc_quote類型,那么 //調用的全部都是Quote::net_price(size_t ) vector<Quote> itemVec; for(size_t i = 0;i != 10; ++i){Disc_quote item("isbn-001-v1",12.5,100,12);itemVec.push_back(item); } double sum = 0; for(vector<Quote>::iterator it = itemVec.begin(); it != itemVec.end(); ++ it){sum += it->net_price(10); } cout << sum << endl;return 0; }15.29
int main() { //結果發現,定義時候的類型是Quote,即使插入的元素類型是Disc_quote類型,那么 //調用的全部都是Quote::net_price(size_t ) vector<shared_ptr<Quote>> itemVec; for(size_t i = 0;i != 10; ++i){Disc_quote item("isbn-001-v1",12.5,100,12);itemVec.push_back(make_shared<Disc_quote>(item)); } double sum = 0; for(vector<shared_ptr<Quote>>::iterator it = itemVec.begin(); it != itemVec.end(); ++ it){sum += (*it)->net_price(10); } cout << sum << endl;return 0; }15.8.1節基礎內容?
#include <string> #include <vector> #include <iostream> #include <memory> #include <set> using namespace std; class Quote{ public:Quote() = default;Quote(const string &book,double sales_price):bookNo(book),b(sales_price){}const string & isbn()const{return bookNo; } private://static可以用戶不完全類型,如果不定義為static類型會報錯//猜測,名字解析先編譯申明部分,但是此時compareIsbn是不完全類型//不完全類型有時候不能當做一個類型使用static bool compareIsbn(const Quote &lhs,const Quote &rhs){return lhs.isbn() < rhs.isbn();}string bookNo;int b;multiset<shared_ptr<Quote>,decltype(compareIsbn)*> item{compareIsbn}; //上面不用用圓括號()}; int main() {return 0; }上面的代碼有2個問題需要思考:
(1)為何compareIsbn一定定義成static的,如果去掉static會報錯。如下錯誤:
2.cc:23:50: error: invalid use of non-static member function ‘bool Quote::compareIsbn(const Quote&, const Quote&)’23 | multiset<shared_ptr<Quote>,decltype(compareIsbn)*> item{compareIsbn};| ^ 2.cc:17:9: note: declared here17 | bool compareIsbn(const Quote &lhs,const Quote &rhs)| ^~~~~~~~~~~ 2.cc:23:52: error: template argument 2 is invalid23 | multiset<shared_ptr<Quote>,decltype(compareIsbn)*> item{compareIsbn};| ^ 2.cc:23:70: error: cannot convert ‘<brace-enclosed initializer list>’ to ‘int’ in initialization23 | multiset<shared_ptr<Quote>,decltype(compareIsbn)*> item{compareIsbn};猜測,可能是解析multiset<shared_ptr<Quote>,decltype(compareIsbn)*>時候,compare是不完全類型。
(2) .......item{compareIsbn},這里不能用圓括號。如果換成(),會按照如下方式報錯:
2.cc:26:59: error: ‘compareIsbn’ is not a type26 | multiset<shared_ptr<Quote>,decltype(compareIsbn)*> item(compareIsbn);練習15.23?
// 15.7.cpp : 定義控制臺應用程序的入口點。 // #include "stdafx.h" #include <string> #include <iostream> #include <memory> using namespace std; class Base{ public:virtual int fcn(){cout << "Base::fcn()" << endl;return 0;}private:};class D1 : public Base{ public:int fcn()override{cout << "D1::fcn()" << endl;return 0;}private:};class D2 : public D1{ public:int fun(int){ cout << "D2::fcn(int)" << endl; }virtual int fcn()override{cout << "D2:fcn()" << endl;return 0;}private:};int _tmain(int argc, _TCHAR* argv[]) {Base bobj; D1 d1obj; D2 d2obj;Base *bp1 = &bobj, *bp2 = &d1obj, *bp3 = &d2obj;bp1->fcn();bp2->fcn();bp3->fcn();D1 *d1p = &d1obj; D2 *d2p = &d2obj;cout << "can you see it?" << endl;system("pause");return 0; }15.24哪種類需要虛析構函數?虛析構函數需要執行什么樣的操作?
解:
作為基類使用的類應該具有虛析構函數,以保證在刪除指向動態分配的對象的基礎類指針時,根據指針實際指向的對象所屬的類型運行適當的析構函數。
? ? ? ? 虛析構函數可以為空,即不執行任何操作。一般而言,析構函數的主要作用是清楚本類中定義的數據成員。如果該類沒有定義指針類成員,則使用合成的版本即可;如果該類定義了指針成員,則一般需要自定義析構函數以對指針成員進行適當的清楚。因此,如果有虛析構函數必須執行的操作,則就是清楚本類中定義的數據成員的操作。
練習15.26答案
//文件 main.cc #include <string> #include <iostream> #include "quote.h" using namespace std;int main() { Quote q1; Bulk_quote d1;return 0; } //文件functions.cc #include <string> #include <iostream> #include <memory> #include "quote.h" using namespace std;string Quote::isbn()const{return bookNo; }double Quote::net_price(size_t n)const {return n * price; }double print_total(ostream &os,const Quote &item,size_t n) //override:顯式地用這個成員函數改寫基類的虛函數 { //根據傳入的item的形參的對象類型調用Quote::net_price 或者 Bulk_quote::net_price //共有成員訪問的時候必須用對象去訪問。 double ret = item.net_price(n); os << "ISBN: " << item.isbn() << "# sold: " << n << " total due: " << ret << endl;return ret; }//destructor Quote::~Quote(){cout << "Quote::~Quote()" << endl;}Bulk_quote::Bulk_quote(const string &book,double p,size_t qty,double disk):Disc_quote(book,p,qty,disk) {cout << "Bulk_quote(const string &,double ,size_t ,double)" << endl; }double Bulk_quote::net_price(size_t n)const {return 1.1; }//test void call_fun(Quote q) {cout << q.net_price(12) << endl;} //文件quote.h #ifndef QUOTE_INCLUDE_H #define QUOTE_INCLUDE_H#include <string> #include <iostream> using namespace std;class Bulk_quote; class Quote{ public:Quote(){cout << "Quote()" << endl;};Quote(const string &book,double sales_price):bookNo(book),price(sales_price){cout << "Quote(const string &,double )" << endl;}Quote(const Quote & q):bookNo(q.bookNo),price(q.price){cout << "Quote(const Quote &)" << endl;}Quote(Quote &&q):bookNo(std::move(q.bookNo)),price(q.price){cout << "Quote(Quote &&)" << endl;}Quote& operator=(const Quote &q){bookNo = q.bookNo;price = q.price;cout << "Quote::operator=" << endl;return *this; }Quote& operator=(Quote &&q){bookNo = std::move(q.bookNo);price = q.price;cout << "Quote::operator=(Quote&&)" << endl;return *this; }string isbn()const;virtual double net_price(size_t n)const;friend double print_total(ostream &os,const Quote &item,size_t n);virtual ~Quote(); private:string bookNo; protected:double price = 0.0; };//abstract class class Disc_quote: public Quote{public:Disc_quote(){cout << "Disc_quote()" << endl;}Disc_quote(const string &book,double price,size_t qty,double disc):Quote(book,price),quantity(qty),discount(disc){cout << "Disc_quote(const string&,double ,size_t ,double) " <<endl;}Disc_quote(const Disc_quote &q):Quote(q),quantity(q.quantity),discount(q.discount){cout << "Disc_quote(const Disc_quote &)" << endl; }Disc_quote(Disc_quote &&q):Quote(std::move(q)),quantity(q.quantity),discount(q.discount){}Disc_quote & operator=(const Disc_quote &d){Quote::operator=(d);quantity = d.quantity;discount = d.discount; cout << "Disc_quote::operator=" << endl;return *this;}Disc_quote& operator=(Disc_quote &&d){Quote(std::move(d));quantity = d.quantity;discount = d.discount;return *this;}//pure virtual functiondouble net_price(size_t)const = 0;~Disc_quote()override{cout << "~Disc_quote()" << endl; }private: size_t quantity = 0; double discount = 0;}; class Bulk_quote : public Disc_quote //Bulk_quote derived from Quote { public:Bulk_quote(){cout << "Bulk_quote()" << endl; }Bulk_quote(const string &,double,size_t ,double);//拷貝控制成員Bulk_quote(const Bulk_quote &b):Disc_quote(b){cout << "Bulk_quote(const Bulk_quote &)" << endl;}Bulk_quote(Bulk_quote && b):Disc_quote(std::move(b)){cout << "Bulk_quote(Bulk_quote&&)" << endl;}Bulk_quote& operator=(const Bulk_quote &b){Disc_quote::operator=(b); cout <<"Bulk_quote(const Bulk_quote &)" << endl;return *this;}Bulk_quote& operator=(Bulk_quote &&q){Disc_quote::operator=(std::move(q));return *this; }~Bulk_quote()override{cout << "~Bulk_quote()" << endl;}//覆蓋基類的版本以實現大量購買的折扣和政策double net_price(size_t n)const override; //override:顯式地用這個成員函數改寫基類的虛函數};double print_total(ostream &,const Quote &,size_t ); void call_fun(Quote ); #endif15.30
//main.cc #include <string> #include <iostream> #include "quote.h" #include <vector> #include <memory>using namespace std; int main() {return 0; } //functions.cc #include <string> #include <iostream> #include <memory> #include "quote.h" using namespace std;string Quote::isbn()const{return bookNo; }double Quote::net_price(size_t n)const {cout << "Quote::net_price(size_t)" << endl;return n * price; }double print_total(ostream &os,const Quote &item,size_t n) //override:顯式地用這個成員函數改寫基類的虛函數 { //根據傳入的item的形參的對象類型調用Quote::net_price 或者 Bulk_quote::net_price //共有成員訪問的時候必須用對象去訪問。 double ret = item.net_price(n); os << "ISBN: " << item.isbn() << "# sold: " << n << " total due: " << ret << endl;return ret; }//destructor Quote::~Quote(){cout << "Quote::~Quote()" << endl;}/* Bulk_quote::Bulk_quote(const string &book,double p,size_t qty,double disk):Disc_quote(book,p,qty,disk) {cout << "Bulk_quote(const string &,double ,size_t ,double)" << endl; } */double Bulk_quote::net_price(size_t n)const {cout << "Bulk_quote::net_price(size_t)" << endl; return 1.1; }//test void call_fun(Quote q) {cout << q.net_price(12) << endl;}//Basket類成員函數 double Basket::total_receipt(ostream &os)const { double sum = 0.0; for(auto iter = items.cbegin();iter != items.end();iter = items.upper_bound(*iter)){sum += print_total(os,**iter,items.count(*iter));} os << "Total sale: " <<sum <<endl;return sum; } //quote.h #ifndef QUOTE_INCLUDE_H #define QUOTE_INCLUDE_H#include <string> #include <iostream> #include <vector> #include <memory> #include <set> using namespace std;class Bulk_quote; class Quote{ public:Quote(){cout << "Quote()" << endl;};Quote(const string &book,double sales_price):bookNo(book),price(sales_price){cout << "Quote(const string &,double )" << endl;}Quote(const Quote & q):bookNo(q.bookNo),price(q.price){cout << "Quote(const Quote &)" << endl;}Quote(Quote &&q):bookNo(std::move(q.bookNo)),price(q.price){cout << "Quote(Quote &&)" << endl;}Quote& operator=(const Quote &q){bookNo = q.bookNo;price = q.price;cout << "Quote::operator=" << endl;return *this; }Quote& operator=(Quote &&q){bookNo = std::move(q.bookNo);price = q.price;cout << "Quote::operator=(Quote&&)" << endl;return *this; }string isbn()const;virtual double net_price(size_t n)const;friend double print_total(ostream &os,const Quote &item,size_t n);virtual ~Quote();virtual Quote* clone() const &{return new Quote(*this); }virtual Quote* clone()&&{return new Quote(std::move(*this));}private:string bookNo; protected:double price = 0.0; };//abstract class class Disc_quote: public Quote{public:Disc_quote(){cout << "Disc_quote()" << endl;}Disc_quote(const string &book,double price,size_t qty,double disc):Quote(book,price),quantity(qty),discount(disc){cout << "Disc_quote(const string&,double ,size_t ,double) " <<endl;}Disc_quote(const Disc_quote &q):Quote(q),quantity(q.quantity),discount(q.discount){cout << "Disc_quote(const Disc_quote &)" << endl; }Disc_quote(Disc_quote &&q):Quote(std::move(q)),quantity(q.quantity),discount(q.discount){}Disc_quote & operator=(const Disc_quote &d){Quote::operator=(d);quantity = d.quantity;discount = d.discount; cout << "Disc_quote::operator=" << endl;return *this;}Disc_quote& operator=(Disc_quote &&d){Quote(std::move(d));quantity = d.quantity;discount = d.discount;return *this;}//pure virtual functiondouble net_price(size_t)const = 0;~Disc_quote()override{cout << "~Disc_quote()" << endl; }private: size_t quantity = 0; double discount = 0;}; class Bulk_quote : public Disc_quote //Bulk_quote derived from Quote { public:Bulk_quote* clone()const & override{return new Bulk_quote(*this); }Bulk_quote* clone()&& override{return new Bulk_quote(std::move(*this));}using Disc_quote::Disc_quote;Bulk_quote(){cout << "Bulk_quote()" << endl; }//Bulk_quote(const string &,double,size_t ,double);//拷貝控制成員Bulk_quote(const Bulk_quote &b):Disc_quote(b){cout << "Bulk_quote(const Bulk_quote &)" << endl;}Bulk_quote(Bulk_quote && b):Disc_quote(std::move(b)){cout << "Bulk_quote(Bulk_quote&&)" << endl;}Bulk_quote& operator=(const Bulk_quote &b){Disc_quote::operator=(b); cout <<"Bulk_quote(const Bulk_quote &)" << endl;return *this;}Bulk_quote& operator=(Bulk_quote &&q){Disc_quote::operator=(std::move(q));return *this; }~Bulk_quote()override{cout << "~Bulk_quote()" << endl;}//覆蓋基類的版本以實現大量購買的折扣和政策double net_price(size_t n)const override; //override:顯式地用這個成員函數改寫基類的虛函數};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();} // items定義成一個multiset,可以將同一本書的多條交易信息保存到一個multiset中, // 這里不能用圓括號,因為default member initializer來初始化成員有2 // 個方法,list initialization,即一對花括號.第二種是copy initializa//tion,即 等號.如果嘗試用圓括號,編譯器會誤以為是函數聲明,所以/報告compare is not a type..multiset<shared_ptr<Quote>,decltype(compare)*> items{compare}; };double print_total(ostream &,const Quote &,size_t ); void call_fun(Quote );#endif練習15.32
Query未定義自己的拷貝、移動、賦值和銷毀成員,當進行這些操作時,執行默認的語義。而其唯一的數據成員是Query_base的shared_ptr,因此,當拷貝、移動、賦值、或者銷毀一個Query對象時,會調用shared_ptr的對應控制成員,從而實現多個Query對象正確共享一個Query_base。而shared_ptr的控制成員調用Query_base的控制成員時,由于指向的可能是Query_base的派生類對象,因此可能是類層次中進行相應的拷貝、移動操作,調用Query_base的派生類的相應控制成員。
練習15.34
? (a)Query(const string &),WordQuery(const string &),NotQuery(const Query &),AndQuery(const Query &,const Query &),OrQuery(const Query &,const Query &)。
? (b)OrQuerry::rep()
(c)OrQuery::eval(const TextQuery &)
15.35
15.9節
先看一些代碼的基礎,下面是查詢類的完整代碼
?
練習15.34答案:
(a)執行的構造函數如下:
WordQuery(const string &)Query(const string &)WordQuery(const string &)Query(const string &)WordQuery(const string &)Query(const string &)AndQuery(const Query &,const Query &,const string &)BinaryQuery(const Query &,const Query &,const string? &)#下面這個構造函數形式參數類型不可以是const的Query(shard_ptr<Query_base> )OrQuery(const Query &,const Quyer &)BinaryQuery(const Query &,const Query &,const string &)(b)
首先執行Query::rep() 然后直行OrQuery::rep() ...Query::rep() ...Query::rep() ...AndQuery::rep() ...Query::rep() ...WordQuery::rep() ...WordQuery::rep() ...WordQuery::rep()(c)
Query::eval() OrQuery::eval() Query::eval() Query::eval() AndQuery::eval() Query::eval() Query::eval() Query::eval() WordQuery::eval() WordQuery::eval() WordQuery::eval()15.37(這個題答案是自己想的,做了一下午,從下午1點做到5點30)
自己隨便弄了個測試的讀取單詞的數據文件:
文件data.txt
you are my good friend , and you are so good too . i am glad to see you , if you do not mind i will take some time to study from you .wahaha . sometimes i was so moody . //main.cc #include <string> #include <iostream> #include "textquery.h" #include "query.h" #include <fstream> using namespace std; int main() {ifstream in("data.txt"); TextQuery t(in);shared_ptr<Query_base> p = ~shared_ptr<Query_base>(new WordQuery("you"))|shared_ptr<Query_base>(new WordQuery("are"));print(cout,p->eval(t));print(cout,p) << endl; return 0; } //textquery.cc#include <string> #include <iostream> #include <fstream> #include <vector> #include <map> #include <set> #include "textquery.h" #include <memory> #include <sstream> using namespace std; TextQuery::TextQuery(ifstream &in):file(new vector<string>()) { string line; //cnt 表示讀入文件的行號 int cnt = 0; while(getline(in,line)){//首先把行插入到文件file中file->push_back(line);istringstream in_s(line);string word;while(in_s >> word){//在wm中插入單詞的行號//注意這里ret是引用類型auto &ret = wm[word];if(!ret){//為ret分配內存空間,這里無論有沒有圓括號都是空集合初始化ret所指向的空間ret.reset(new set<line_no>()); }ret->insert(cnt);} ++ cnt; //行號遞增 } }QueryResult TextQuery::query(const string &s)const { //不能用下標操作查詢單詞s是否存在,因為下標操作在單詞不存在的 //情況下會往map中添加單詞,正確的做法用find auto ret = wm.find(s); //find返回的是迭代器 if(ret == wm.end()){//這里必須新建一個set<line_no>對象,內容為空,不然當單詞查不到時,對返回的QueryResult對象中的有關wm對象的一切操作均會報錯,因為沒有分配內存,是不能使用的,包括調用size()成員函數。//智能指針使用前必須要里面有內容,也就是分配了內存auto nodata = make_shared<set<line_no>>();return QueryResult(file,nodata,s);}//注意迭代器其實就是一個指針,用->訪問迭代器所指的pair else//ret->second 不能改成下標操作,也就是wm[s],原因我也不知道,大概是下標式子使用受限return QueryResult(file,ret->second,s); } //textquery.h#ifndef TEXTQUERY_H #define TEXTQUERY_H #include <string> #include <iostream> #include <fstream> #include <vector> #include <map> #include <set> #include <memory> using namespace std; class QueryResult; class TextQuery{public: using line_no = vector<string>::size_type; TextQuery() = default; TextQuery(ifstream &in); QueryResult query(const string &s)const;private: map<string,shared_ptr<set<line_no>>> wm; shared_ptr<vector<string>> file; };class QueryResult { friend ostream& print(ostream &,const QueryResult &);public: using line_no = TextQuery::line_no; QueryResult() = default; QueryResult(shared_ptr<vector<string>> f,shared_ptr<set<line_no>> l,const string &w):file(f),lines(l),query_word(w){} shared_ptr<vector<string>> get_file()const{return file;} set<line_no>::iterator begin()const{return lines->begin();} set<line_no>::iteratorend()const{return lines->end();} size_t size()const{return lines->size();}private: //編譯器對于智能指針只管擇機釋放,不管分配,所以使用前務必自己分配。 shared_ptr<set<line_no>> lines; //lines使用前必須手動分配空間 shared_ptr<vector<string>> file;//file 使用前必須手動分配空間 string query_word;}; inline ostream& print(ostream & os,const QueryResult &q) { os << q.query_word << " occurs "<<q.lines->size() << " times:" << endl; auto file = q.get_file(); for(auto i : *(q.lines)){os << "(line " << i+1 << ") " << (*file)[i] << endl;}return os; } #endif //query.h#ifndef QUERY_H #define QUERH_H#include <string> #include <iostream> #include <memory>#include <set> #include <algorithm> #include "textquery.h" using namespace std;class Query_base{public:Query_base() = default;virtual QueryResult eval(const TextQuery &t)const=0;virtual string rep()const=0;virtual ~Query_base(){}protected:using line_no = TextQuery::line_no; }; //沒有覆蓋eval,所以BinaryQuery還是一個抽象基類 class BinaryQuery : public Query_base { public:BinaryQuery(shared_ptr<Query_base> l,shared_ptr<Query_base> r,const string & ope):lhs(l),rhs(r),opSym(ope){}string rep()const override;protected: shared_ptr<Query_base> lhs; //使用前必須分配空間,或者是用另一個指針賦值 shared_ptr<Query_base> rhs; //同上 private: string opSym; }; inline string BinaryQuery::rep()const {return "(" + lhs->rep() +" " +opSym + " "+rhs->rep() + ")"; }class WordQuery:public Query_base {public:WordQuery(const string &s):query_word(s){}QueryResult eval(const TextQuery &t)const override{ return t.query(query_word);}string rep()const override{return query_word;} private:string query_word;};class AndQuery:public BinaryQuery {public: //BinaryQuery()的各個實參必須都正確,如果寫成lhs和rhs,那么使用時候就會失去實參, //結果就是lhs和rhs沒有得到空間然后就是用了,就會出現 segmentation fault,core dumped之類的錯誤。 AndQuery(shared_ptr<Query_base> l,shared_ptr<Query_base> r):BinaryQuery(l,r,"&"){} QueryResult eval(const TextQuery &)const override;};inline QueryResult AndQuery::eval(const TextQuery &t)const { auto l = lhs->eval(t); auto r = rhs->eval(t); auto file = l.get_file(); auto new_set = make_shared<set<line_no>>(); //set_intersection算法:intersection意思是 交集 ,所以set_intersection是求交集 set_intersection(l.begin(),l.end(),r.begin(),r.end(),inserter(*new_set,new_set->begin()));return QueryResult(file,new_set,rep()); }class OrQuery:public BinaryQuery {public:OrQuery(shared_ptr<Query_base> l,shared_ptr<Query_base> r):BinaryQuery(l,r,"|"){}QueryResult eval(const TextQuery &)const;string rep()const; }; inline QueryResult OrQuery::eval(const TextQuery &t)const { auto r1 = lhs->eval(t); auto r2 = rhs->eval(t); auto file = r1.get_file(); auto new_set = make_shared<set<line_no>>(r1.begin(),r1.end()); new_set->insert(r2.begin(),r2.end()); return QueryResult(file,new_set,rep()); }string OrQuery::rep()const {return "(" + lhs->rep() +" | "+ rhs->rep() + ")"; }class NotQuery:public Query_base {public: NotQuery(shared_ptr<Query_base> s):query(s){} QueryResult eval(const TextQuery &)const; string rep()const;private: shared_ptr<Query_base> query; };inline string NotQuery::rep()const {return "~(" + query->rep() + ")"; }inline QueryResult NotQuery::eval(const TextQuery &t)const { auto result = query->eval(t); auto file = result.get_file(); auto old_set = make_shared<set<line_no>>(result.begin(),result.end()); auto beg = old_set->begin(); auto end = old_set->end(); auto new_set = make_shared<set<line_no>>(); //下面是求一個子集的補集,并且全集和補集是按照同樣的方式排序時候的一個算法,我自己稱它為: //“求補集算法”,很好用的一個算法 for(int i = 0;i != file->size(); ++i){if(beg != end && i != *beg){new_set->insert(i);}else{if(beg != end)++ beg; }} return QueryResult(file,new_set,rep()); }inline shared_ptr<Query_base>operator&(shared_ptr<Query_base> l,shared_ptr<Query_base> r) {return shared_ptr<Query_base>(new AndQuery(l,r)); }inline shared_ptr<Query_base>operator|(shared_ptr<Query_base> l,shared_ptr<Query_base> r) {return shared_ptr<Query_base>(new OrQuery(l,r)); }inline shared_ptr<Query_base> operator~(shared_ptr<Query_base> l) {return shared_ptr<Query_base>(new NotQuery(l)); }inline ostream & print(ostream &os,shared_ptr<Query_base> p) {return os << p->rep(); }#endif運行結果是:
(~(you) | are) occurs 1 times: (line 1) you are my good friend , and you are so good too . (~(you) | are)成功~~~
回顧:
如果運行后發現如下結果:
segmentation fault,core dumped.
那么對于本題的程序,很大程度上可能是指針沒有智能沒有分配空間就使用了,然后就會發生這種問題。
然后,本程序有以下幾個地方需要去檢查:
? ? ? ? ? ? ?程序中已經做提示了
15.38? 答:
(a)不合法,& 運算符返回的類型是 Query?類型,不能轉換為BinaryQuery類型。而且BinaryQuery是抽象基類,不能定義自己類型對象。
(b)不合法,& 運算符返回的類型是 Query?類型,不能轉換為BinaryQuery類型。
(c)不合法,道理同(b)
提醒:要解決這個問題,必須收先自己想想,如果類有動態內存,而類的成員也有動態內存的時候,析構函數應該怎么寫?
//textquery.cc #include <string> #include <iostream> #include <fstream> #include <vector> #include <map> #include <set> #include "textquery.h" #include <memory> #include <sstream> using namespace std; TextQuery::TextQuery(ifstream &in):file(new vector<string>()) { string line; //cnt 表示讀入文件的行號 int cnt = 0; while(getline(in,line)){//首先把行插入到文件file中file->push_back(line);istringstream in_s(line);string word;while(in_s >> word){//在wm中插入單詞的行號//注意這里ret是引用類型auto &ret = wm[word];if(!ret){//這里無論有沒有括號都是空集合ret.reset(new set<line_no>()); }ret->insert(cnt);} ++ cnt; //行號遞增 } }QueryResult TextQuery::query(const string &s)const { //不能用下標操作查詢單詞s是否存在,因為下標操作在單詞不存在的 //情況下會往map中添加單詞,正確的做法用find auto ret = wm.find(s); //find返回的是迭代器 if(ret == wm.end()){//這里必須新建一個set<line_no>對象,內容為空,不然//對此時返回的QueryResult對象中的有關wm對象的一切操作均會報錯,因為沒有分配內存,是不能使用的。//智能指針使用前必須要里面有內容,也就是分配了內存auto nodata = make_shared<set<line_no>>();return QueryResult(file,nodata,s);}//注意迭代器其實就是一個指針,用->訪問迭代器所指的pair elsereturn QueryResult(file,ret->second,s); } query.cc //query.h中的函數都定義為內聯函數了,這里空了 #include <string> #include <iostream> #include <memory> #include <vector> #include <map> #include <set> #include "query.h" using namespace std; //main.cc #include <string> #include <iostream> #include "query.h" #include "textquery.h" #include <fstream> #include <set> #include <memory> using namespace std;int main() { ifstream in("data.txt"); TextQuery t(in); Query q = Query("my") & Query("good") | Query("you"); cout << q.rep() << endl; QueryResult ret = q.eval(t); print(cout,ret);return 0; } //textquery.h #ifndef TEXTQUERY_H #define TEXTQUERY_H#include <string> #include <iostream> #include <fstream> #include <vector> #include <map> #include <set> #include <memory> using namespace std; class QueryResult; class TextQuery{public: using line_no = vector<string>::size_type;TextQuery() = default; TextQuery(ifstream &in); QueryResult query(const string &s)const;private:map<string,shared_ptr<set<line_no>>> wm; shared_ptr<vector<string>> file; };class QueryResult { friend ostream& print(ostream &,const QueryResult &);public: using line_no = TextQuery::line_no; QueryResult() = default; QueryResult(shared_ptr<vector<string>> f,shared_ptr<set<line_no>> l,const string &w):file(f),lines(l),query_word(w){} shared_ptr<vector<string>> get_file()const{return file;} set<line_no>::iterator begin()const{return lines->begin();} set<line_no>::iteratorend()const{return lines->end();} size_t size()const{return lines->size();}private: shared_ptr<set<line_no>> lines; shared_ptr<vector<string>> file; string query_word;}; inline ostream& print(ostream & os,const QueryResult &q) { os << q.query_word << " occurs "<<q.lines->size() << " times:" << endl; auto file = q.get_file(); for(auto i : *(q.lines)){os << "(line " << i+1 << ") " << (*file)[i] << endl;}return os; }#endif //query.h/************************************************ 1.constructor may not be cv-qualified(構造函數不能是 const的),道理很簡單,構造函數會改變對象的值* 2.constructor may not be ref-qualified(構造函數不可以加& 或者 &&,賦值運算符卻可以)* //道理也簡單,構造函數是定義對象時候用,定義的對象當然不分左值和右值,也就是在定義對象之前* //對象還不存在,也就是說定義對象之前根本沒東西,當然就不能分左值和右值了。* //其它函數是建立在有對象的基礎上使用的,當然這個對象就可以是左值或者右值了。* //一句話:“只有成員函數或者賦值運算符才可以是& 或者 &&的”,構造函數不在此,構造函數不在此列列* 3.構造函數不能分左值和右值版本,賦值運算符在本程序中不要分左值和右值版本,只要給參數類型分* 左值版本和右值版本就可以了,也就是說遇到左值/右值類型的參數(具體賦值過程中是右操作數),調用對應版本即可,左操作數不需要定義左右版本* 4.構造函數和賦值運算符都不可以定義成const的函數,因為它們都需要改變對象本身* 5.拷貝賦值運算符:首先需要把右側值存起來,存的時候就存必要的成員還是整體全部* 其次需要釋放左側資源,調用左側類型的的析構函數就可以了吧,還是要把左側的基類部分 和非基類部分分開釋放。* 最后給左側賦值。** 暫時結論:析構的時候就析構左側部分就可以。析構函數會自動遞歸調用基類的析構函數* 釋放基類部分,但是賦值或者拷貝的時候必須要首先拷貝基類部分或者給基類部分賦值。* ** 6.析構函數的安排* 7.在類的對象的析構中,必須手動釋放類的資源,對于普通的內置類型的值,不用做任何事情。* 8.類值版本的賦值運算符定義的時候,左側對象的析構能否直接調用左側對象的析構函數?還是必須只手動析構動態內存成員就可以,但是如果普通成員是類類型,也包含動態內存成員又該怎么辦呢?此時是調用左側對象的析構函數呢?還是只釋放動態內存成員* 9.賦值運算符能不能這樣工作,直接調用拷貝構造函數,新建個對象把右側對象放進來,然后再釋放左側對象(能不能直接調用左側對象的西溝函數),然后再把左側值賦予右側的值。* 10.可能有點眉目了,如果在賦值運算符中左側對象調用對象本身析構函數,可能會導致,賦值之前左側對象就失效了...為了防止這類事情發生,只能是逐個成員地去釋放即可.* 11.所有的純虛函數必須都要有定義,因為運行時候才能知道調用哪個版本?如果不給虛函數定義,那么申明一個純虛函數。否則直接繼承父類的虛函數。* ***********************************************///友元,構造函數,拷貝構造成員,(虛函數覆蓋)其它成員函數,析構函數,數據成員 #ifndef QUERY_H #define QUERY_H#include <string> #include <iostream> #include <memory> #include <vector> #include <map> #include <set> #include "textquery.h" #include <algorithm>using namespace std;class Query_base;class WordQuery; class Binary_query;class NotQuery; class AndQuery; class OrQuery; class Query;/*類值版本的*/ class Query_base { friend class Query; protected:using line_no = TextQuery::line_no;//共有5個虛函數(成員函數):eval,rep,2 clones,~Query_base virtual QueryResult eval(const TextQuery &)const = 0; virtual string rep()const = 0; virtual Query_base *clone() && = 0; virtual Query_base *clone()const & = 0;protected: virtual ~Query_base()=default; //析構函數可不可以protected?};class WordQuery :public Query_base{friend class Query;private:WordQuery(const string &s):query_word(s){}protected:WordQuery(const WordQuery &); //constructors may not be ref-qualified WordQuery(WordQuery && ); //constructors may not be ref-qualified WordQuery & operator=(const WordQuery &); WordQuery & operator=(WordQuery && );//覆蓋虛函數 QueryResult eval(const TextQuery &t) const override {return t.query(query_word);} string rep()const override {return query_word;} WordQuery * clone()&& override; WordQuery * clone()const& override;~WordQuery();private: string query_word; };//拷貝控制成員 inline WordQuery::WordQuery(const WordQuery &w):query_word(w.query_word){} inline WordQuery::WordQuery(WordQuery && w):query_word(std::move(w.query_word)){} inline WordQuery & WordQuery::operator=(const WordQuery &w) {Query_base::operator=(w);//只是個形式,為了說明語法形式,實際不起作用query_word = w.query_word;return *this; } inline WordQuery & WordQuery::operator=(WordQuery && w) {Query_base::operator=(std::move(w)); //可不可以這樣使用?光竊取基類部分,不會破壞基類部分外的部分嗎?query_word = std::move(w.query_word);return *this; }inline WordQuery * WordQuery::clone()const& {return new WordQuery(*this);//克隆就是把自己復制一次 } inline WordQuery * WordQuery::clone()&& {return new WordQuery(std::move(*this)); //復制自己,調用自己的拷貝構造函數 }//WordQuery類型析構的時候,只要析構在內存中自己本身即可。 inline WordQuery::~WordQuery(){}class Query {public: friend Query operator&(const Query &,const Query &); friend Query operator|(const Query &,const Query &); friend Query operator~(const Query &);//構造函數public: Query(const string &s); //constructors may not be cv-qualified (構造函數用限定符是不合法的) //限定符就是const和volatile//拷貝控制成員 //note:constructors may not be ref-qualified(構造函數不能加引用限定符) Query(const Query &); Query(Query &&); Query & operator=(const Query &); Query & operator=(Query &&);QueryResult eval(const TextQuery &)const; string rep()const; //clone函數/*開始Query類自己定義了下面兩個函數,后來發現是多余的,Query的clone函數只是他自己用與克隆自己的query部分,完全可以直接調用自己的query的clone就可以。此時如果clone是各個類的私有成員函數,只需要Query是各個類的友元即可 Query_base* clone()const &; Query_base* clone()&&; *///析構函數 ~Query();private: Query_base * query; //請確定這里用不用重新分配內存 Query(Query_base * q):query(q){} //主要是三個函數使用,不能是explicit的 };//構造函數 inline Query::Query(const string &s):query(new WordQuery(s)){}//拷貝控制成員 inline Query::Query(const Query &s):query(s.query->clone()){} //拷貝構造函數inline Query::Query(Query &&s):query(s.query->clone()){s.query == nullptr;} //移動構造函數函數,調用右值版本的cloneinline Query & Query::operator=(const Query &s) {//必須能正確處理自賦值的情況auto new_data = s.query->clone(); //新開辟了內存,現在new_data中有了s中的內容//開始自己是這樣寫的,后來發現不妥this->~Query(); ,改成下面的delete query;query = new_data;return *this; } inline Query & Query::operator=(Query && s) {auto new_data = s.query->clone(); //為了正確處理自賦值情況,先把右側值暫時存起來//這么寫會破壞整個對下你給this->~Query();delete query;query = new_data; //最后給左側對象賦值return *this;} inline Query::~Query(){//利用派生類對象的指針調用基類的虛函數,就會發生動態綁定delete query; //虛調用:利用基類指針調用虛析構函數}inline QueryResult Query::eval(const TextQuery &t)const {return query->eval(t); } inline string Query::rep()const {return query->rep(); }class BinaryQuery:public Query_base{friend class Query;protected:BinaryQuery(const Query &l,const Query &r,const string & o):lhs(l),rhs(r),opSym(o){}BinaryQuery(const BinaryQuery &);BinaryQuery(BinaryQuery &&); //constructors may not be ref-BinaryQuery&operator=(const BinaryQuery &);BinaryQuery&operator=(BinaryQuery &&);string rep()const;~BinaryQuery();Query lhs,rhs;private:string opSym;};inline BinaryQuery::BinaryQuery(const BinaryQuery &b):lhs(b.lhs),rhs(b.rhs),opSym(b.opSym){} inline BinaryQuery::BinaryQuery(BinaryQuery &&b):lhs(std::move(b.lhs)),rhs(std::move(b.rhs)),opSym(std::move(b.opSym)){}inline BinaryQuery& BinaryQuery::operator=(const BinaryQuery &b) { //這里不用提前釋放lhs和rhs,因為lhs和rhs有自己的拷貝賦值運算符。賦值時候會調用它們的拷貝賦值運算符 //先釋放左邊對象,然后再賦值 Query_base::operator=(b); //調用直接基類的拷貝賦值運算符,給直接基類部分賦值 lhs = b.lhs; //會調用lhs和rhs的拷貝賦值運算符,進行賦值 rhs = b.rhs; opSym = b.opSym;return *this; } inline BinaryQuery& BinaryQuery::operator=(BinaryQuery &&b) { //基類部分該怎么表示Query_base::operator=(std::move(b)); lhs = std::move(b.lhs);rhs = std::move(b.rhs);opSym = std::move(b.opSym);return *this; }inline BinaryQuery::~BinaryQuery(){} //沒有本身動態內存,它也沒有動態分配的成員指針inline string BinaryQuery::rep()const {return "(" + lhs.rep() + opSym + rhs.rep() + ")"; }class AndQuery:public BinaryQuery{friend class Query;friend Query operator&(const Query &,const Query&);public:AndQuery(const AndQuery &);AndQuery(AndQuery &&);AndQuery& operator=(const AndQuery &);AndQuery& operator=(AndQuery&&);private:AndQuery(const Query &l,const Query &r):BinaryQuery(l,r,"&"){}QueryResult eval(const TextQuery &t)const override;AndQuery* clone()const & override;AndQuery* clone()&& override;protected:~AndQuery(){ }; //如果少了這句,會怎么樣?};inline AndQuery::AndQuery(const AndQuery &a):BinaryQuery(a) {}inline AndQuery::AndQuery(AndQuery &&a):BinaryQuery(std::move(a)){}inline AndQuery& AndQuery::operator=(const AndQuery &a) {//賦值和構造函數過程必須給直接基類賦值或者構造,每個構造函數必須負責直接基類部分的構造BinaryQuery::operator=(a);//調用基類的拷貝賦值運算符給基類部分賦值return *this; } inline AndQuery& AndQuery::operator=(AndQuery && a) {BinaryQuery::operator=(std::move(a)); //調用基類部分的移動賦值運算符移動基類部分//AndQUery 沒有自己的數據成員,這里為空return *this; }inline QueryResult AndQuery::eval(const TextQuery &t)const { auto left = lhs.eval(t),right = rhs.eval(t); auto new_file = left.get_file(); auto new_set = make_shared<set<line_no>>(); set_intersection(left.begin(),left.end(),right.begin(),right.end(),inserter(*new_set,new_set->begin()));return QueryResult(new_file,new_set,rep()); }inline AndQuery * AndQuery::clone()const & {return new AndQuery(*this); //復制自己,調用自己的拷貝構造函數 }inline AndQuery * AndQuery::clone()&& {return new AndQuery(std::move(*this)); //復制自己,調用自己的拷貝構造函數 }class OrQuery:public BinaryQuery{friend class Query; friend Query operator|(const Query &,const Query &);OrQuery(const Query &l,const Query &r):BinaryQuery(l,r,"|"){} QueryResult eval(const TextQuery &)const; OrQuery * clone() const &; OrQuery * clone()&&;public:OrQuery(const OrQuery &); //constructors may not be cv-qualified(不能加限定符const) OrQuery(OrQuery &&); OrQuery& operator=(const OrQuery &); OrQuery& operator=(OrQuery &&);protected: ~OrQuery(){} //不希望普通用戶使用這個函數};//OrQuery沒有自己的數據成員,只要拷貝直接基類部分即可 inline OrQuery::OrQuery(const OrQuery &o):BinaryQuery(o) {} inline OrQuery::OrQuery(OrQuery && o):BinaryQuery(std::move(o)) {} inline OrQuery& OrQuery::operator=(const OrQuery &o ) {BinaryQuery::operator=(o);return *this; } inline OrQuery& OrQuery::operator=(OrQuery && o) {BinaryQuery::operator=(std::move(o));return *this; }inline QueryResult OrQuery::eval(const TextQuery &t)const { auto left = rhs.eval(t),right = rhs.eval(t); auto l = left.get_file(); auto r = right.get_file(); auto new_set = make_shared<set<line_no>>(); //這里不加圓括號會報錯的 set_intersection(left.begin(),left.end(),right.begin(),right.end(),inserter(*new_set,new_set->begin()));return QueryResult(l,new_set,rep()); } inline OrQuery * OrQuery::clone()const& {return new OrQuery(*this); } inline OrQuery * OrQuery::clone()&& {return new OrQuery(std::move(*this)); }class NotQuery:public Query_base{friend class Query;friend Query operator~(const Query &);private: Query query; //唯一的一個數據成員 NotQuery(const Query &q):query(q){}NotQuery(const NotQuery &); NotQuery(NotQuery &&); //copy constructor and copy assignment operator may not be const //拷貝構造函數和拷貝賦值運算符都不能是const的,因為要改變對象的成員 NotQuery& operator=(const NotQuery &); NotQuery& operator=(NotQuery &&);protected: //虛函數在這個類中是什么角色?取決于直接基類和間接基類的定義 //虛函數如果沒有定義自己版本,會繼承最近的那個基類的版本 //string rep()const;直接基類已經定義了一個可用的版本,這里會直接繼承 //rep()會先繼承最親的那個基類的代碼 QueryResult eval(const TextQuery &)const override; string rep()const override{return "~(" + query.rep() + ")";} NotQuery * clone()const & override; NotQuery * clone()&& override; ~NotQuery();}; inline //必須慎重 NotQuery::NotQuery(const NotQuery &n):query(n.query){} inline NotQuery::NotQuery(NotQuery &&n):query(std::move(n.query)){}inline NotQuery& NotQuery::operator=(const NotQuery &n) {Query_base::operator=(n);query = n.query;return *this; } inline NotQuery& NotQuery::operator=(NotQuery && n) {Query_base::operator=(std::move(n));query = std::move(n.query);return *this; }inline QueryResult NotQuery::eval(const TextQuery &t)const { auto ret = query.eval(t); auto file = ret.get_file(); auto new_set = make_shared<set<line_no>>(); auto n = file->size(); //file是一個vector的shared_ptr對象 auto iter = ret.begin(); for(int i = 0; i != n; ++i){if(i != *iter || iter == ret.end())new_set->insert(i);else{if(iter != ret.end())++ iter;}} return QueryResult(file,new_set,rep()); }inline NotQuery * NotQuery::clone()const & {return new NotQuery(*this); }inline NotQuery * NotQuery::clone()&& {return new NotQuery(std::move(*this)); }inline NotQuery::~NotQuery(){}//沒有自己的動態成員inline Query operator&(const Query &l,const Query &r) {return new AndQuery(l,r); } inline Query operator|(const Query &l,const Query &r) {return new OrQuery(l,r); }inline Query operator~(const Query& q) {return new NotQuery(q); } #endif?文件data.txt是外部讀取單詞的,自己隨意編寫的,內容如下:
i am a good boy and you are a good boy too if you want to improve your c++ programming language you should write codes everyday best wishes?在g++中執行代碼
g++ main.cc query.cc textquery.cc -o 123?執行文件 123
./123結果如下:
((my&good)|you) ((my&good)|you) occurs 3 times: (line 1) i am a good boy and you are a good boy too (line 2) if you want to improve your c++ programming language (line 4) you should write codes everyday?未完待續。。。
補充一點知識:讀者自己看吧,這點知識我自己作為初學者體會的,只可意會,不可言傳。
#include <string> #include <iostream> using namespace std; class A{public:A() = default;virtual void print()const{cout << "A::print()" << endl;}int a = 111; int b = 11;}; class B : public A {public:B() = default;void print()const override{cout << "B::print()" << endl;}void f()const{cout << "B::f()" << endl;}private:int c= 222;int d = 22;};int main() { B b; A * pa = &b; pa->print(); A & ra = *pa; ra.print(); A * pb = &ra; pb->print(); pb->f();return 0; }運行結果:
1.cc: In function ‘int main()’: 1.cc:48:5: error: ‘class A’ has no member named ‘f’48 | pb->f();| ^用我自己的語言來說,如果一旦把一個派生類對象放在了基類的引用或者指針上,那么這個對象再也不能通過后續的(直接賦值或者把指針解引用賦值,不能用new或者make_shared重新分配內存)操作賦值給派生類對象了。而且,無論這個對象接下來通過多少次的解引用或者直接賦值,只要都是經過指針或者引用傳遞,那么它終究還是一個動態類型是派生類行的對象,而且靜態類型始終都是A類型。
要注意下面2個結論:
1.除了動態綁定之外,那么其它部分都是按照靜態類型來解析的,比如說“不存在基類向派生類的類型轉換。”
2.如果用new或者make_shared分配內存。那么無論這個對象的靜態類型和動態類型是否一致,那么始終結果都是按照靜態類型來分配的。也就是除了動態綁定的事實,其余都是按照以前靜態類型來操作的。也試試說,調用new(或者make_shared)分配新的內存,靜態類型和動態類型不一致的對象被破壞。new分配的結果的對象中,動態類型和靜態類型是一致的(結果都是靜態對象類型)。
3.再進一步,只要一直是用引用、指針、解引用+指針(把一個靜態類型和動態類型不一致的類型先解引用再綁定到基類的指針上),那么“動態綁定”的事實就可以一直持續下去。(千萬不要中途去new或者make_shared)
4.關于更多相關內容,參見如下百度百科鏈接
dynamic_cast_百度百科
把上面這個程序作如下改動,可以發現一個更有趣的事實:
#include <string> #include <iostream> #include <memory> using namespace std; class A{public:A() = default;virtual void print()const{cout << "A::print()" << endl;}int a = 111; int b = 11;}; class B : public A {public:B() = default;void print()const override{cout << "B::print()" << endl;}void f()const{cout << "B::f()" << endl;}private:int c= 222;int d = 22;};int main() { B b; A * pa = &b; pa->print(); A & ra = *pa; ra.print(); A * pb = &ra; pb->print(); //pb->f(); //這句代碼是錯誤的,發生動態綁定的時候,只能調用虛函數,不能調用普通成員。 A * pc = new A(*pa); //調用new分配內存,動態類型對象被破壞。new分配的結果對象pc中,動態類型和靜態類型是一致的。 //下面一句錯誤更厲害,g++編譯器會直接報如下錯誤: //no matching function for call to 'B::B(&A)',也就是B類中不存在參數類型是A&的構造函數 A * pd = new B(*pa); pc -> print(); shared_ptr<A> pd = make_shared<A>(*pa); pd->print();return 0; }g++編譯結果如下:
1.cc: In function ‘int main()’: 1.cc:51:19: error: no matching function for call to ‘B::B(A&)’51 | A * pd = new B(*pa);| ^ 1.cc:21:3: note: candidate: ‘constexpr B::B()’21 | B() = default;| ^ 1.cc:21:3: note: candidate expects 0 arguments, 1 provided 1.cc:18:7: note: candidate: ‘constexpr B::B(const B&)’18 | class B : public A| ^ 1.cc:18:7: note: no known conversion for argument 1 from ‘A’ to ‘const B&’ 1.cc:18:7: note: candidate: ‘constexpr B::B(B&&)’ 1.cc:18:7: note: no known conversion for argument 1 from ‘A’ to ‘B&&’ 1.cc:53:15: error: conflicting declaration ‘std::shared_ptr<A> pd’53 | shared_ptr<A> pd = make_shared<A>(*pa);| ^~ 1.cc:51:5: note: previous declaration as ‘A* pd’51 | A * pd = new B(*pa);| ^~關于dynamic的一個例子,如下:
B * pc = dynamic_cast<B*>(pb); pc -> print(); pc->f();B &rb = dynamic_cast<B&>(ra); rb.print(); rb.f();總結
以上是生活随笔為你收集整理的c++primer 5th第15章基础、课后习题自己解析、心得体会等的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c++ primer 第14章 习题解答
- 下一篇: c++继承构造函数