C++ Primer中文版(第四版)-讀書筆記【PartB】

來源:互聯網
上載者:User

 

上接PartA

 

p387 初始化列表
建構函式具有名字、形參表和函數體,例如
Sales_item::Sales_item(const string &book):isbn(book),units_sold(0),revenue(0.0){}
其中,省略初始化列表並在建構函式內對資料成員賦值是合法的,例如可以修改為:

Sales_item::Sales_item(const string &book){ isbn=book; units_sold=0; revenue=0.0;} 

 

p388 建構函式執行兩個階段
初始化階段(類類型的資料成員總是在初始化階段初始化)
普通的計算階段(計算階段由建構函式函數體重所有語句組成)

p389
初始化const或參考型別資料成員的唯一機會實在建構函式初始化列表中,如果ci為const,則
ConstRef::ConstRef(int ii):i(ii),ci(i),ri(ii){}
必須對任何const或參考型別成員以及沒有預設建構函式的類類型的任何成員使用初始化式

p410 禁止複製
為了防止複製,類必須顯示聲明其複製建構函式為private,類的友元和成員仍然可以進行複製,如果要完全禁止,則聲明複製建構函式而不對其定義

p413 三法則
如果類需要解構函式,則它也需要賦值操作符和複製建構函式,這是一個有用的經驗法則,俗稱三法則

p431 重載操作符必須具有一個類類型運算元
int operator+(int,int); //wrong,內建的整型加號操作符不能重定義
Sales_item operator+(const Sales_item&, const Sales_item&);//right,重載操作符
不能為任何內建類型定義額外的新的操作符,例如,不能定義接受兩個數群組類型運算元的operator+

p443 成員訪問操作符
箭頭操作符必須定義類成員函數,解引用操作符不要求定義為成員,但將它作為成員一般也是正確的。

p447 區別操作符首碼與尾碼
尾碼式操作符函數接受一個額外的int型形參,使用尾碼式操作符時,編譯器提供0作為這個形參的實參。那個形參不是尾碼式操作符的正常工作所需要的,它的唯一目的是使尾碼函數與首碼函數區別開來。

p450 函數對象定義
定義了叫用作業符的類,其對象常稱為函數對象,即他們是行為類似函數的對象,例如
absInt absObj;
unsigned int ui=absObj(i); //調用absInt::operator(int)
函數叫用作業必須聲明為成員函數,一個類可以定義函數叫用作業符的多個版本,由形參的數目或類型加以區別

class GT_cls{ public: GT_cls(size_t val=0):bound(val){} bool operator()(const string &s){return s.size()>=bound;} private: std::string::size_type bound;};

使用時直接調用GT_cls類型的對象,尋找單詞數多於6個的情況
cout<<count_if(words.begin(),words.end(),GT_cls(6))<<"words 6 characters or longer"<<endl;

p455 轉換操作符
轉換函式必須是成員函數,不能指定傳回型別,並且形參表必須為空白,例如

class SmallInt{ public: ...//建構函式 operator int() const{return val;} //轉換操作符,必須放在類中,放在類外部為錯 private: std::size_t val;};

同時轉換操作符通過應定義為const,因為該函數一般不改變被轉換的對象

p461 函數重載三步
1、確定候選函數集合:這些是與被調用函數同名的函數
2、選擇可行函數:這些是形參數目和類型與函數調用的實參相匹配的候選函數。選擇可行函數時,如果有轉換操作,編譯器還要確定哪個轉換操作來匹配每個形參
3、選擇首選函數。對將實參轉換為對應形參所需的類型轉換進行分類,對於類類型的實參和形參,可能的轉換的集合包括類類型轉換

p462-465 函數中的二義性問題

避免二義性的最好方法是。保證最低只有一種途徑將一個類型轉換為另一個類型。最好的方法是限制轉換操作符的數目,尤其是到一種內建類型應該只有一個轉換。

多個轉換

class SmallInt{ public: operator int() const {return val;} operator double() const{return val;} private: std::size_type val;};void compute(int);void compute(double);void compute(long double);SmallInt si;compute(si);//出現二義性,可以用int,也可以通過轉換為double,可修改為compute(static_cast<int>(si));

 

多個轉換建構函式

class SmallInt{ public: SmallInt(int=0);};class Integral{ public: Integral(int =0);};void manip(const Integral&);void manip(const SmallInt&);manip(10);//二義性,任意一個類都可以與manip的一個版本匹配,修改為manip(SmallInt(10));

 

操作符二義性

class SmallInt{ SmallInt(int=0); operator int() const{return val;} friend SmallInt operator+ (const SmallInt&, const SmallInt&); private: std::size_t val;};SmallInt s1,s2;SmallInt s3=s1+s2; //使用+操作int i=s3+0; //二義性,可以將0轉換為SmallInt類型,也可以將s3轉換為int類型使用int內建加操作符

 

p475 protected
衍生類別只能通過衍生類別對象訪問其基類的protected成員,衍生類別對其基類類型對象的protected成員沒有特殊的存取權限

p480 多態性
引用和指標的靜態類型和動態類型可以不同,這是C++用以支援多態性的基石
通過基類引用或指標調用基類中定義的函數時,並不知道執行函數的對象的確切類型,執行函數的對象可能是基類類型的,也可能是衍生類別型的
如果調用非虛函數,則無論實際對象什麼類型,都執行基類類型所定義的函數,如果調用虛函數,則直到運行時才能確定調用哪個函數

p488 引用轉換不同於轉換對象
可以將衍生類別型的對象傳給希望接受基類引用的函數,但在講對象傳給函數之前,引用直接綁定到該對象,實際上實參是該對象的引用,對象本身並未複製,該對象仍是衍生類別型對象。
將衍生類別對象傳給希望接受基類類型對象(不是引用)的函數時,形參的類型是固定的,在編譯和運行時形參都是基類類型對象,如果衍生類別型調用這樣的函數,則該衍生類別對象的基類部分被複製到形參
一個是將衍生類別對象轉換為基類類型引用,一個是用衍生類別對象對基類對象進行初始化或賦值

p494 複製控制
如果衍生類別定義了自己的複製建構函式,則該複製建構函式一般應顯式使用基類複製建構函式初始化對象的基類部分

class Base{/*...*/};class Derived:public Base{ Derived(const Derived& d):Base(d) /*...*/{/*...*/};};

如果沒有Base(d),效果是運行Base的預設建構函式初始化對象的基類部分,導致衍生類別中基類的部分是預設值,而派產生員是另一個對象副本

p497 建構函式虛函數
如果在建構函式或解構函式中調用虛函數,則啟動並執行是為建構函式或解構函式自身類型定義的版本

p499 範圍
在衍生類別範圍中衍生類別成員將屏蔽基類成員,即使函數原型不同,基類成員也會屏蔽

struct Base{ int menfcn();};struct Derived: Base{ int menfcn(int);};Derived d;Base b;b.menfcn(); //調用Based.menfcn(10); //調用Derivedd.menfcn(); //錯誤,沒有相對應匹配,編譯器一旦找到名字相同就不再尋找d.Base::menfcn();//調用Base 

衍生類別中定義的函數也不重載基類中定義的函數,通過衍生類別對象調用函數時,實參必須與衍生類別中定義的版本匹配,只有衍生類別根本沒有定義該函數時,才考慮基類函數。

p501 基類調用虛函數
通過基類類型的引用或指標調用函數

struct Base{ public: virtual int fcn();};class D1:public Base{ public: int fcn(int);};//D1中有兩個函數,一個Base中的虛函數,一個自己定義的函數class D2:public D1{ public: int fcn(int); int fcn();};//D2重定義它繼承的兩個函數Base bobj;D1 d1obj;D2 d2obj;Base *bp1=&bobj, *bp2=&d1obj,*bp3=&d2obj;bp1->fcn();//調用Basebp2->fcn();//調用Basebp3->fcn();//調用D2

p528 inline函數模板
template <typename T> inline T min(const T&,const T&);

p533 非形參模板類型
在調用函數時非形參將用值代替,值的類型在模板形參表中指定,例如

template <class T,size_t N> void array_init(T(&parm)[N]){ for(size_t i=0;i!=N;++i) {  parm[i]=0; }}int x[42];double y[10];array_init(x);//轉換為 array_init(int(&)[42]);array_init(y);//轉換為array_init(double(&)[10]);

使用非類型形參指定數組的長度

p538 類類型轉換受限
一般而言,不會轉換實參以匹配已有的執行個體化,相反,會產生新的執行個體。除了產生新的執行個體以為,編譯器只會執行兩種轉換
1、const轉換:接受const引用或const指標的函數可以分別用非const對象的引用或指標來調用,無須產生新的執行個體化。如果函數接受非參考型別,形參和實參都忽略const,無論傳遞const或非const對象給接受非參考型別的函數,都使用相同的執行個體化
2、數組或函數到指標的轉換:如果模板形參不是參考型別,則對數組或函數類型的實參應用常規指標轉換。數組實參將當做指向其第一個元素的指標,函數實參當做指向函數類型的指標

p552 類模板中友元
三種友元聲明
1、普通非模板類或函數的友元聲明,將友元關係授予明確指定的類或函數

template <class Type> class Bar{ friend class FooBar; friend void fcn();};

2、類模板或函數模板的友元聲明,授予對友元所有執行個體的訪問權

template <class Type> class Bar{ template <class T> friend class Foo1; template <class T> friend void temp1_fcn1(const T&);};

 

3、只授予對類模板或函數模板的特定執行個體的訪問權的友元聲明

template <class T> class Foo2;template <class T> void temp1_fcn2(const &T);template <class T> class Bar{ friend class Foo2<char*>; friend void temp1_fcn2<char*>(char * const &);};

 

p556 成員模板
在類內部定義

template <class Type> class Queue{ public: template <class Iter> void assign(Iter,Iter);};

在類外部定義

template <class T> template <class Iter>void Queue<T>::assign(Iter beg,Iter end){ //...}

在類外部定義時,必須包含類模板形參以及自己的模板形參,第一個模板形參表是類模板的,第二個模板形參表是成員模板的

p565 函數模板特化
該定義中一個或多個模板形參的實際類型或實際值是指定的

template <>int compare<const char*>(const char* const &v1,const char* const &v2){ //...}

當調用compare版本時,傳給它兩個字元指標,編輯器將調用特化版本,為其他實參類型調用泛型版本

const char *cp1="word",*cp2="hi";int i1,i2;compare(cp1,cp2); //調用特化版本compare(i1,i2);//調用泛型版本

 

p571 函數匹配與函數模板
確定函數中既有普通函數又有函數模板,確定函數調用步驟如下
1、為這個函數名建立後選函數集合,包括
與被調用的函數名字相同的任意普通函數
任意函數模板執行個體化,模板實參推斷髮現了與調用中所用的函數實參相匹配的模板實參
2、確定哪些普通函數是可行的。候選集合中的每個模板執行個體都是可行的
3、如果需要轉換來進行調用,根據轉換的種類排列可行函數,調用模板函數執行個體所允許的轉換是有限的
如果只有一個函數可選,就使用它
如果調用有二義性,從可行函數集合中去掉所有函數模板執行個體
4、重新排列去點函數模板執行個體的可行函數
如果只有一個函數可選,就使用它
否則調用有二義性

p584 異常匹配
在尋找匹配的catch期間,找到的catch不必是與異常最匹配的那個catch,相反,將選中第一個找到的可以處理異常的catch。除了以下幾種情況外,異常的類型與catch的說明符必須完全符合
允許從非const到const的轉換,非const對象的throw可以與指定接受const引用的catch匹配
允許從衍生類別型到基類類型的轉換
將數群組轉換為指向數群組類型的指標,將函數轉換為指向函數類型的適當指標

p585 重新拋出
catch可以通過重新拋出將異常傳遞給調用鏈更上層的函數
throw;
被拋出的異常是原來的異常對象,而不是catch自己的形參。當catch形參是基類類型的時候,拋出的實際類型取決於異常對象的動態類型,而不是catch形參的靜態類型。一般而言catch可以改變它的形參,在改變它的形參以後,如果重新拋出異常,只有當異常說明符是引用的時候,才會傳播那些改變

catch (my_error &eobj){ eobj.status=severErr; throw; //拋出的severErr}catch (my_error eobj){ eobj.status=severErr; throw; //拋出的沒有改變}

 

p611 實參相關的尋找

std::string s;getline(std::cin ,s);//調用std::getline(std::istream&, const std::string &)

接受類類型形參的函數,以及與類本身定義在同一命名空間中的函數,在用類類型對象作為實參時是可見的
調用getline在當前範圍中,包含調用的範圍以及cin的類型和string類型的命名空間中尋找匹配的函數

p623 虛繼承
虛繼承是一種機制,類通過虛繼承指出它希望共用器虛基類的狀態,在虛繼承下,對給定虛基類,無論該類在派生層次中作為虛基類出現多少次,只繼承一個共用的基類子物件。共用的基類子物件稱為虛基類。

class istream:: public virtual ios{...};class ostream:: virtual public ios{...};class iostream:: public istream,public ostream{...};

 

p624 虛基類二義性
假定通過多個派生路徑繼承名為x的成員
1、如果在每個路徑中x表示同一虛基類成員,沒有二義性,因為共用該成員的單個執行個體
2、如果在某個路徑中x是虛基類的成員,而在另一路徑中x是後代衍生類別的成員,也沒有二義性,特定衍生類別執行個體的優先順序高於共用虛基類執行個體
3、如果沿每個繼承路徑x表示後代衍生類別的不同成員,則該成員的直接存取是二義性的

 

p625 虛基類初始化
在虛派生中,由最低層的衍生類別的建構函式初始化虛基類。任何直接或間接繼承虛基類的類一般也必須為該基類提供自己的初始化式,只要可以建立虛基類衍生類別類型的獨立對象,該類就必須初始化自己的虛基類。

p647
可以使用dynamic_cast操作符將基類類型對象的引用或指標轉換為同一繼承層次中其他類型的引用或指標,如果轉換到指標類型失敗,則結果是0,轉換到參考型別失敗,拋出異常

p649
typeid問一個運算式是什麼類型

typeid(e)//e是任意運算式或類型名if(typeid(*bp)==typeid(*dp)){...}

 

p653 類成員指標

class Screen{... private: std::string contents;};

contents的完全類型是Screen類的成員,其類型是std::string,可以修改為

string Screen::*ps_Screen; //定義指向Screen類的string成員的指標string Screen::*ps_Screen=&Screen::contents; //將contents的地址初始化ps_Screen

 

p671 extern"C"函數指標
extern "C" void (*pf)(int);
通過pf調用函數時,假定調用的是一個C函數調用而編譯該函數
C函數的指標與C++函數的指標具有不同的類型,不能將C函數的指標初始化或賦值為C++函數的指標,反之亦然

void (*pf1)(int); //C++extern "C" void (*pf2)(int);//Cpf1=pf2;//錯誤

 

一點感想:陸陸續續花了半個月的時間把這本C++經典入門教材看完了,怎麼說呢,感覺閱讀之前還是最好要對C++有點基礎,因為這本書並沒有花太大筆墨來嘮叨繼承和多態性,而是直接把物件導向編程和泛型程式設計結合起來講。我想第四部分應該是本書最重要也是最難的地方,肯定也是C++的精華所在,當前只是看過一遍,有點瞭解罷了,以後肯定還要再看,通過實踐加深印象才行。至於第五部分用處其實不是很多,除了異常外幾乎不怎麼用。不過瞭解一下肯定也是好的。既然看完這本了,下一本書應該是《C++程式設計語言》了,看看那本書能提升到什麼樣的層次,期待吧。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.