<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          C++對象的底層原理都在這兒了,還敢說學(xué)不會?

          共 10999字,需瀏覽 22分鐘

           ·

          2021-12-31 11:36

          Part1一、關(guān)于對象

          C 語言是程序性的,語言本身并沒有支持數(shù)據(jù)和函數(shù)之間的關(guān)聯(lián)性C++ 中可能采取抽象數(shù)據(jù)類型,或者是多層次的類結(jié)構(gòu)完成 C++ 的封裝并沒有增加多少成本,每一個成員函數(shù)雖然在class中聲明,但是卻不出現(xiàn)在每個對象中 每一個非內(nèi)聯(lián)的成員函數(shù)只會誕生一個函數(shù)實例 每個內(nèi)聯(lián)函數(shù)會在其每一個使用者身上產(chǎn)生一個函數(shù)實例

          C++ 在布局以及存儲時間上主要的額外負擔(dān)是由virtual引起的 虛函數(shù)機制用以支持一個有效率的“執(zhí)行期綁定” 虛基類用來實現(xiàn)“多次出現(xiàn)在繼承關(guān)系中的基類,有一個單一而被共享的實例

          還有一些多重繼承下的額外負擔(dān),發(fā)生在一個派生類和其第二或后繼之基類的轉(zhuǎn)換之間

          1.1 C++對象模式

          C++對象模型有以下幾點非靜態(tài)數(shù)據(jù)成員放在類對象內(nèi)靜態(tài)數(shù)據(jù)成員放在類對象靜態(tài)和非靜態(tài)成員函數(shù)也放在類對象虛函數(shù)則不同 每個類中存放一個指針稱為vptr,指向虛函數(shù)表表中每個都指向一個虛函數(shù)

          C++對象模型

          1.2 關(guān)鍵詞所帶來的差異

          int ( *pq ) ( ); //聲明當(dāng)語言無法區(qū)分那是一個聲明還是一個表達式時,我們需要一個超越語言范圍的規(guī)則,而該規(guī)則會將上述式子判斷為一個“聲明“

          struct和class可以相互替換,他們只是默認的權(quán)限不一樣如果一個程序員需要擁有C聲明的那種struct布局,可以抽出來單獨成為struct聲明,并且和C++部分組合起來

          1.3 對象的差異

          C++支持三種程序范式:程序模型、抽象數(shù)據(jù)類型模型、面向?qū)ο竽P?/strong>面向?qū)ο竽P驮诶^承體系中 ,有時候編譯期間無法確定指針或引用所指類型

          C++支持的多態(tài)類型:

          1. 經(jīng)由一組隱式的轉(zhuǎn)化操作:如派生類指針轉(zhuǎn)化為指向父類的指針
          2. 經(jīng)由虛函數(shù)機制
          3. 經(jīng)由dynamic_cast 和 typeid運算符

          一個class所占的大小包括:其非靜態(tài)成員所占的大小 由于內(nèi)存對齊填補上的大小 加上支持虛函數(shù)而產(chǎn)生的大小

          指針的類型,只能代表其讓編譯器如何解釋其所指向的地址內(nèi)容,和它本身類型無關(guān),所以轉(zhuǎn)換其實是一種編譯器指令,不改變所指向的地址,只影響怎么解釋它給出的地址

          當(dāng)一個基類對象被初始化為一個子類對象時,派生類就會被切割用來塞入較小的基類內(nèi)存中,派生類不會留下任何東西,多態(tài)也不會再呈現(xiàn)。

          Part2二、構(gòu)造函數(shù)語意學(xué)

          2.1 默認構(gòu)造函數(shù)的構(gòu)造操作

          以下四種情況下,會合成有用的構(gòu)造函數(shù):帶有默認構(gòu)造函數(shù)的成員函數(shù)對象,不過這個合成操作只有在構(gòu)造函數(shù)真正需要被調(diào)用時才發(fā)生,但只是調(diào)用其成員的默認構(gòu)造函數(shù),其他則不會初始化如果一個派生類的父類帶有默認構(gòu)造函數(shù),那么子類如果沒有定義構(gòu)造函數(shù),則會合成默認構(gòu)造函數(shù),如果有的話但是沒有調(diào)用父類的,則編譯器會插入一些代碼調(diào)用父類的默認構(gòu)造函數(shù)帶有一個虛函數(shù)的類類聲明(或繼承)一個虛函數(shù) 類派生自一個繼承串鏈,其中有一個或更多的虛基類帶有一個虛基類的類

          C++新手常見的兩個誤解任何class如果沒有定義默認構(gòu)造函數(shù),就會被合成出來一個編譯器合成出來的默認構(gòu)造函數(shù)會顯式設(shè)定類中的每一個數(shù)據(jù)成員的額 默認值

          2.2 拷貝構(gòu)造函數(shù)的構(gòu)造操作

          有三種情況會調(diào)用拷貝構(gòu)造函數(shù):對一個對象做顯式的初始化操作當(dāng)對象被當(dāng)作參數(shù)交給某個函數(shù)當(dāng)函數(shù)傳回一個類對象時

          如果類沒有聲明一個拷貝函數(shù),就會有隱式的聲明和隱式的定義出現(xiàn),同默認構(gòu)造函數(shù)一樣在使用時才合成出來 什么情況下一個類不展現(xiàn)“淺拷貝語意”:當(dāng)類內(nèi)含有一個成員類而后者的類聲明中有一個拷貝構(gòu)造函數(shù)(例如內(nèi)含有string成員變量) 當(dāng)類繼承自一個基類而基類中存在拷貝構(gòu)造函數(shù)這兩個編譯器都會合成拷貝構(gòu)造函數(shù)并且安插進那個成員和基類的拷貝構(gòu)造函數(shù)當(dāng)類聲明了一個或多個虛函數(shù)編譯器會顯式的設(shè)定新類的虛函數(shù)表,而不是直接拷貝過來指向同一個當(dāng)類派生自一個繼承串鏈,其中有一個或多個虛基類編譯器會合成一個拷貝構(gòu)造函數(shù),安插一些代碼用來設(shè)定虛基類指針和偏移的初值,對每個成員執(zhí)行必要的深拷貝初始化操作,以及執(zhí)行其他的內(nèi)存相關(guān)工作

          2.3 程序轉(zhuǎn)化語意學(xué)

          在將一個類作為另一個類的初值情況下,語言允許編譯器有大量的自由發(fā)揮的空間,用來提升效率,但是缺點是不能安全的規(guī)劃拷貝構(gòu)造函數(shù)的副作用,必須視其執(zhí)行而定

          拷貝構(gòu)造的應(yīng)用,編譯器會多多少的進行部分轉(zhuǎn)換,尤其是當(dāng)一個函數(shù)以值傳遞的方式傳回一個對象,而該對象有一個合成的構(gòu)造函數(shù),此外編譯器也會對拷貝構(gòu)造的調(diào)用進行調(diào)優(yōu),以額外的第一參數(shù)取代NRV(Named Return Value)

          2.4 成員們的初始化隊伍

          四種情況下你需要使用成員初始化列表當(dāng)初始化一個引用成員變量當(dāng)初始化一個const 成員變量當(dāng)調(diào)用一個基類的構(gòu)造函數(shù),而它擁有一組參數(shù)當(dāng)調(diào)用一個類成員變量的構(gòu)造函數(shù),而它擁有一組參數(shù)

          class?Word{
          ?String?_name;
          ?int?_cnt;
          public:
          ?Word(){
          ??_name?=?0;
          ??_cnt?=?0;
          ?}
          /*使用成員列表初始化可以解決????
          ?????Word()?:?_name(0),_cnt(0){

          ?}
          */
          ?
          }

          上式不會報錯,但是會有效率問題,因為這樣會先產(chǎn)生一個臨時的string對象,然后將它初始化,之后以一個賦值運算符將臨時對象指定給_name,再摧毀臨時的對象

          成員初始化列表中的初始化順序是按照類中的成員變量聲明的順序,與成員初始化列表的排列順序無關(guān)

          Part33、Data語意學(xué)

          class?X{};
          class?Y?:?public?virtual?X?{};
          class?Z?:?public?virtual?X?{};
          class?A?:?public?Y,public?Z?{};

          sizeof(X)?//1
          sizeof(Y)?//4
          sizeof(Z)?//4
          sizeof(A)?//8

          X為1是因為編譯器的處理,在其中插入了1個char,為了讓其對象能在內(nèi)存中有自己獨立的地址Y,Z是因為虛基類表的指針A 中含有Y和Z所以是8

          每一個類對象大小的影響因素:非靜態(tài)成員變量的大小virtual特性內(nèi)存對齊

          3.1 數(shù)據(jù)成員的綁定

          如果類的內(nèi)部有typedef,請把它放在類的起始處,因為防止先看到的是全局的和這個typedef相同的沖突,編譯器會選擇全局的,因為先看到全局的

          3.2 數(shù)據(jù)成員的布局

          非靜態(tài)成員變量的在內(nèi)存中的順序和其聲明順序是一致的但是不一定是連續(xù)的,因為中間可能有內(nèi)存對齊的填補物 virtual機制的指針所放的位置和編譯器有關(guān)

          3.3 成員變量的存取

          靜態(tài)變量都被放在一個全局區(qū),與類的大小無關(guān),正如對其取地址得到的是與類無關(guān)的數(shù)據(jù)類型,如果兩個類有相同的靜態(tài)成員變量,編譯器會暗自為其名稱編碼,使兩個名稱都不同非靜態(tài)成員變量則是直接放在對象內(nèi),經(jīng)由對象的地址和在類中的偏移地址取得,但是在繼承體系下,情況就會不一樣,因為編譯器無法確定此時的指針指的具體是父類對象還是子類對象

          3.4 繼承下的數(shù)據(jù)成員

          在下面給定的兩個類中依次討論不同情況:

          原本的數(shù)據(jù)模型

          在單一繼承沒有虛函數(shù)的情況下布局圖

          單一繼承且無虛函數(shù)

          這種情況下常見錯誤:可能會重復(fù)設(shè)計一些操作相同的函數(shù),我們可以把某些函數(shù)寫成inline,這樣就可以在子類中調(diào)用父類的某些函數(shù)來實現(xiàn)簡化把數(shù)據(jù)放在同一個類中和繼承起來的內(nèi)存布局可能不同,因為每個類需要內(nèi)存對齊

          分層繼承的布局

          可見內(nèi)存大了100%

          容易出現(xiàn)的不易發(fā)現(xiàn)的問題:

          繼承下易犯錯誤

          當(dāng)加上多態(tài)之后,對空間上增加的額外負擔(dān)包括:

          導(dǎo)入一個虛函數(shù)表,表中的個數(shù)是聲明的虛函數(shù)的個數(shù)加上一個或兩個slots(用來支持運行類型識別)在每個對象中加入vptr,提供執(zhí)行期的鏈接,使每一個類能找到相應(yīng)的虛函數(shù)表加強構(gòu)造函數(shù),使它能夠為vptr設(shè)定初值,讓它指向?qū)?yīng)的虛函數(shù)表,這可能意味著在派生類和每一個基類的構(gòu)造函數(shù)中,重新設(shè)定vptr的值加強析構(gòu)函數(shù),使它能夠消抹“指向類的相關(guān)虛函數(shù)表”的vptr,vptr很可能以及在子類析構(gòu)函數(shù)中被設(shè)定為子類的虛表地址。析構(gòu)函數(shù)的調(diào)用順序是反向的,從子類到父類

          以下是三種情況:不同的繼承下會有不同的布局

          vptr放在前端

          單一繼承有虛函數(shù)

          多重繼承

          單一繼承特點:派生類和父類對象都是從相同的地址開始,區(qū)別只是派生類比較大能容納自己的非靜態(tài)成員變量

          多重繼承下會比較復(fù)雜

          多重繼承關(guān)系

          一個派生對象,把它的地址指定給最左邊的基類,和單一繼承一樣,因為起始地址是一樣的,但是后面的需要更改,因為需要加上前面基類的大小,才能得到后面基類的地址

          多重繼承數(shù)據(jù)分布

          虛繼承

          STL標準庫中使用的虛繼承:

          虛繼承例子

          虛繼承關(guān)系:

          虛繼承數(shù)據(jù)在內(nèi)存中的分布

          虛繼承數(shù)據(jù)模型2

          3.5 對象成員的效率

          程序員如果關(guān)心程序效率,應(yīng)該實際測試,不要光憑推論、常識判斷或假設(shè)。優(yōu)化操作并不一定總是能夠有效運行,我不止一次以優(yōu)化方式來 編譯一個已通過編譯的正常程序,卻以失敗收場

          3.6 指向數(shù)據(jù)成員的指針

          vptr通常放在起始處或尾端,與編譯器有關(guān),C++標準允許放在類中的任何位置 取某個類成員變量的地址,通常取到得的是在類的首地址的偏移位置例如?& Point3d::z;?將得到在類的偏移位置,最低限度是類的成員大小總和,而這個偏移量通常都被加上了1如果用一個真正綁定類對象(也就是使用 . 操作符訪問成員變量)去取地址,得到的將會是內(nèi)存中真正的地址

          在多重繼承下,若要將第二個積累的指針和一個與派生類綁定的成員結(jié)合起來,那么將會因為需要加入偏移量而變得相當(dāng)復(fù)雜

          Part4四、Function 語意學(xué)

          C++支持三種類型的成員函數(shù):static 、non-static 、virtualstatic函數(shù)限制不能直接存取non-static數(shù)據(jù)不能被聲明為const

          4.1 成員函數(shù)的各種調(diào)用方式

          非靜態(tài)成員函數(shù):C++會保證至少和一般的普通的函數(shù)有相同的效率,經(jīng)過三個步驟的轉(zhuǎn)換改寫函數(shù),安插一個額外的參數(shù)到該函數(shù)中,用來提供一個存取管道------即this指針對每一個非靜態(tài)成員的存取操作改成使用this指針來調(diào)用將成員函數(shù)改寫成一個外部函數(shù),并且名稱改為獨一無二的

          虛函數(shù)成員函數(shù):也會經(jīng)過類似的轉(zhuǎn)化例如:ptr->normalize()會被轉(zhuǎn)化為( * ptr->vptr[1] )( ptr )vptr是編譯器產(chǎn)生的指針,指向虛函數(shù)表,其名稱也會被改為獨一無二1 是該函數(shù)在虛函數(shù)表中的索引ptr 則是this指針

          靜態(tài)成員函數(shù):它沒有this指針,因此會有以下限制它不能直接存取類中的非成員變量它不能夠被聲明為const、volatile 和 virtual它不需要經(jīng)過類的對象才能被調(diào)用----雖然很多事情況是這樣調(diào)用的

          4.2 詳解虛成員函數(shù)

          虛函數(shù)一般實現(xiàn)模型:每一個類都有一個虛函數(shù)表,內(nèi)含該類中有作用的虛函數(shù)地址,然后每個對象有一個虛函數(shù)指針,指向虛表位置多態(tài)含義:以一個基類的指針(或引用),尋址出一個子類對象什么是積極多態(tài)?

          當(dāng)被指出的對象真正使用時,多態(tài)就變成積極的了

          單一繼承虛函數(shù)布局圖

          單一繼承虛函數(shù)圖

          一個類派生自Point會發(fā)生什么事?三種可能的情況它可以繼承基類所聲明的虛函數(shù)的函數(shù)實例該函數(shù)實例的地址會被拷貝進子類的虛表的相對應(yīng)的slot之中它可以使用自己的虛函數(shù)實例----它自己的函數(shù)實例地址必須放在對應(yīng)的slot之中它可以加入一個新的虛函數(shù),這時候虛函數(shù)表的尺寸會增加一個slot,而新加入的函數(shù)實例地址也會被放進該slot之中

          多重繼承下的虛函數(shù)

          多繼承虛函數(shù)圖

          虛擬繼承下的虛函數(shù)

          虛繼承下的虛函數(shù)圖

          4.4指向成員函數(shù)的指針

          對于普通的成員函數(shù),編譯器會將其轉(zhuǎn)化為一個函數(shù)指針,然后使用成員函數(shù)的地址去初始化

          例如 ?double (Point :: *pmf ) ( );轉(zhuǎn)化為??double ( Point :: coord )( ) = &Point :: x;這樣調(diào)用??(coord) (& origin)或 (coord)(ptr)

          對一個虛函數(shù)取地址,在vc編譯器下,要么得到vacll thunk地址(虛函數(shù)時候),要么得到的是函數(shù)地址(普通函數(shù))

          4.5 內(nèi)聯(lián)函數(shù)

          inline只是向編譯器提出一個請求,是否真的優(yōu)化取決于編譯器自己的判定對于形式參數(shù),會采用:常量表達式替換 常量替換 引入臨時變量來避免多次求值操作

          對于局部變量,會采用:使用臨時變量

          Part5五、構(gòu)造、析構(gòu)、拷貝語意學(xué)

          應(yīng)注意的一些問題:構(gòu)造函數(shù)不要寫為純虛函數(shù),因為當(dāng)抽象類中有數(shù)據(jù)的時候,將無法初始化把所有函數(shù)設(shè)計成虛函數(shù),再由編譯器去除虛函數(shù)是錯誤的,不應(yīng)該成為虛函數(shù)的函數(shù)不要設(shè)計成虛函數(shù)當(dāng)你無法抉擇一個函數(shù)是否需要為const時,尤其是抽象類,最好不設(shè)置成const

          5.1 "無繼承" 情況下的對象構(gòu)造

          對于沒有初始化的全局變量,C語言中會將其放在一個未初始化全局區(qū),而C++會將所有全局對象初始化對于不需要構(gòu)造函數(shù)、析構(gòu)函數(shù)、賦值操作的類或結(jié)構(gòu)體,C++會將其打上POD標簽,賦值時按照c那樣的位搬運對于含有虛函數(shù)的類,編譯器會在構(gòu)造函數(shù)的開始放入一些初始化虛表和虛指針的操作面對函數(shù)以值方式返回,編譯器會將其優(yōu)化為加入一個參數(shù)的引用方式,避免多次構(gòu)造函數(shù)

          5.2 繼承體系下的對象構(gòu)造

          構(gòu)造函數(shù)會含有大量的隱藏嘛,因為編譯器會進行擴充:記錄在成員初始化列表中的成員數(shù)據(jù)初始化會被放進構(gòu)造函數(shù)的本體,并以成員在類中聲明的順序為順序 如果有一個成員并沒有出現(xiàn)在成員初始化列表中,但是它由一個默認構(gòu)造函數(shù),那么也會被調(diào)用在那之前,如果類對象由虛表指針,它必須被設(shè)定初值,指向適當(dāng)?shù)奶摫?/strong>在那之前所有上一層的基類構(gòu)造函數(shù)必須被調(diào)用,以基類聲明的順序(不是成員初始化列表出現(xiàn)的順序)如果基類被列于成員初始化列表中,那么任何顯式指定的參數(shù)應(yīng)該傳遞過去如果基類沒有被列于基類初始化列表中,而它有默認的構(gòu)造函數(shù),那么就調(diào)用如果基類是多重繼承下的第二個或后繼的基類,那么this指針必須有所調(diào)整在那之前,所有虛基類構(gòu)造函數(shù)必須被調(diào)用,從左到右,從最深到最淺如果類被列于成員初始化列表中,那么如果有任何顯式指定的參數(shù),都應(yīng)該傳遞過去。若沒有列于list中,而類中有一個默認構(gòu)造,亦應(yīng)該調(diào)用此外,類中的每一個虛基類的偏移位置必須在執(zhí)行期可被存取如果類對象是最底層的類,其構(gòu)造函數(shù)可能被調(diào)用,某些用以支持這一行為的機制必須被放進來

          不要忘記在賦值函數(shù)中,檢查自我賦值的情況

          5.3 對象復(fù)制語意學(xué)

          當(dāng)一個類復(fù)制給另一個類時,能采用的有三種方式:什么都不做,會實施默認行為如果有需要,會自動生成一個淺拷貝,至于什么時候需要深拷貝(見第二章講)提供一個拷貝復(fù)制運算符顯式地拒絕把一個類拷貝給另一個

          虛基類會使其復(fù)制操作調(diào)用一次以上,因此我們應(yīng)該避免在虛基類中聲明數(shù)據(jù)成員

          5.5 析構(gòu)語意學(xué)

          什么時候會合成析構(gòu)函數(shù)在類內(nèi)含的成員函數(shù)有析構(gòu)函數(shù)基類含有析構(gòu)函數(shù)

          析構(gòu)的正確順序析構(gòu)函數(shù)的本體首先被執(zhí)行,vptr會在程序員的代碼執(zhí)行前被重設(shè)。如果類擁有成員類對象,而后者擁有析構(gòu)函數(shù),那么他們會以其聲明順序的相反順序被調(diào)用如果類內(nèi)涵一個vptr,現(xiàn)在被重新設(shè)定,指向適當(dāng)?shù)姆e累的虛表如果有任何直接的非虛基類擁有析構(gòu)函數(shù),它們會以其聲明順序的相反順序被調(diào)用如果有任何虛基類擁有析構(gòu)函數(shù),而且目前討論的這個類是最尾端的類,那么它們會以其原來的構(gòu)造順序的相反順序被調(diào)用

          Part6六、執(zhí)行期語意學(xué)

          C++難以從程序源碼看出表達式的復(fù)雜過程,因為你并不知道編譯器會在其中添加多少代碼

          編譯器對不同的對象會做不同的操作:

          對于全局對象:編譯器會在添加__main函數(shù)和_exit函數(shù)(和C庫中的不同),并且在這兩個函數(shù)中對所有全局對象進行靜態(tài)初始化和析構(gòu)

          全局對象編譯器操作

          使用被靜態(tài)初始化的對象,有一些缺點

          如果異常處理被支持,那么那些對象將不能被放置到try區(qū)段之內(nèi)增加了程序的復(fù)雜度

          因此,不建議用那些需要靜態(tài)初始化的全局對象

          對于局部靜態(tài)對象

          會增加臨時的對象用來判斷其是否被構(gòu)造,用來保證在第一次進入含有該靜態(tài)對象的起始處調(diào)用一次構(gòu)造函數(shù),并且在離開文件的時候利用臨時對象判斷是否已經(jīng)被構(gòu)造來決定是否析構(gòu)

          對于對象數(shù)組:(例如Point knots[10]

          如果對象沒有定義構(gòu)造函數(shù)和析構(gòu)函數(shù),那么編譯器只需要分配需要存儲10個連續(xù)空間即可 如果有構(gòu)造函數(shù),且如果有名字,則會分為是否含有虛基類調(diào)用不同的函數(shù)來構(gòu)造,如果沒有名字,例如沒有knots,則會使用new來分配在堆中 當(dāng)聲明結(jié)束時會有類似構(gòu)造的析構(gòu)過程 我們無法在程序中取出一個構(gòu)造函數(shù)的地址

          6.2 new 和 delete 運算符

          對于普通類型變量:例如int *pi = new int(5) 調(diào)用函數(shù)庫中的new運算符if(int *pi = _new (sizeof(int) )) 再配置初值 *pi = 5 對于delete來說 delete pi; 則先進行保護 if( pi != 0) 再調(diào)用delete ?、_delete(pi)

          對于成員對象

          對于Point3d* origin = new Point3d

          實際調(diào)用operator new,其代碼如下

          ????extern?void*?operator?new?(size_t?size){
          ????????if(size?==?0)
          ????????????size?=?1;
          ????????void?*?last_alloc;
          ????????while(!(last_alloc?=?malloc(size))){
          ????????????if(_new_handler)
          ????????????????(?*_new_handler)();
          ????????????else
          ????????????????return?0;
          ????????}
          ????????return?last_alloc;
          ????}

          語言要求每一次對new的調(diào)用都必須傳回一個獨一無二的指針,為了解決這個問題,傳回一個指向默認為1Byte的內(nèi)存區(qū)塊,允許程序員自己定義_new_handler函數(shù),并且循環(huán)調(diào)用

          至于delete也相同

          ????extern?void?operator?delete?(void?*ptr){
          ?????if(ptr)
          ??????free(?(char*)ptr)
          ????}

          對于對象數(shù)組,會在分配的內(nèi)存上方放上cookies,來存儲數(shù)組個數(shù),方便delete調(diào)用來析構(gòu)

          程序員最好避免以一個基類指向一個子類所組成的數(shù)組---如果子類對象比其基類大的話

          解決方式:

          ?for(int?ix?=?0;?ix???Point3d?*p?=?&((Point3d*)ptr)[ix];
          ??delete?p;
          ?}

          程序員必須迭代走過整個數(shù)組,把delete運算符實施與每一個元素身上。以此方式,調(diào)用操作將是virtual。因此,Point3d和Point的析構(gòu)函數(shù)都會實施于每一個對象上

          Placement Operator new的語意

          有一個預(yù)先定義好的重載的new運算符,稱為placement operator new。它需要第二個參數(shù),類型為void*

          形如?Point2w *ptw = new (arena) Point2w,其中arena指向內(nèi)存中的一個區(qū)塊,用以放置新產(chǎn)生出來的Point2w 對象

          ?void*?operator?new(size_t?,?void*?p){
          ??return?p;
          ?}

          如果我們在已有對象的基礎(chǔ)上調(diào)用placement new的話,原來的析構(gòu)函數(shù)并不會被調(diào)用,而是直接刪除原來的指針,但是不能使用delete 原來的指針

          正確的方法應(yīng)該是 :

          ????//錯誤:
          ????delete?p2w;
          ????p2w?=?new(arwna)?Point2w;
          ????//正確:
          ????p2w->Point2w;
          ????p2w?=?new(arena)?Point2w;

          6.3 臨時性對象

          臨時對象在類的表達式并賦值,函數(shù)以值方式傳參等都會產(chǎn)生臨時對象-----而臨時對象會構(gòu)造和析構(gòu),所以會拖慢程序的效率,我們應(yīng)該盡量避免

          Part7七、站在對象模型的頂端

          三個著名的C++語言擴充性質(zhì):模板、異常(EH)、RTTI(runtime type identification)

          7.1 Template

          模板實例化時間線

          當(dāng)編譯器看到模板類的聲明時,什么都不會做,不會進行實例化模板類中明確類型的參數(shù),通過模板類的某個實例化版本才能存取操作即使是靜態(tài)類型的變量,也需要與具體的實例版本關(guān)聯(lián),不同版本有不同的一份如果聲明一個模板類的指針,那么不會進行實例化,但是如果是引用,那么會進行實例化對于模板類中的成員函數(shù),只有在函數(shù)使用的時候才會進行實例化

          模板名稱決議的方法-----即如果非成員函數(shù)在類中調(diào)用,那么會調(diào)用名稱相同的哪個版本:

          會根據(jù)該函數(shù)是否與模板有關(guān)來判斷,如果是已知類型,那么會在定義的范圍內(nèi)直接查找,如果依賴模板的具體類型,那么會在實例化的范圍查找

          7.2 異常處理

          C++異常處理三個主要的語匯組件:

          一個throw子語。它在程序某處發(fā)出一個exception,exception可以說內(nèi)建類型也可以是自定義類型一個或多個catch子句。每一個catch子句都是一個exceotion hander,它用來表示說,這個子句準備處理某種類型exception,并且在封閉的大括號區(qū)段中提供實際的處理程序一個try區(qū)段。它被圍繞一系列的敘述句,這些敘述句可能會引發(fā)catch子句起作用

          當(dāng)一個異常被拋出去,控制權(quán)會從函數(shù)調(diào)用中被釋放出來,并尋找一個吻合的catch子句。如果都沒有吻合者,那么默認的處理例程 terminate()會被調(diào)用,當(dāng)控制權(quán)被放棄后,堆棧中的每一個函數(shù)調(diào)用也就被推離。這個程序被稱為 unwingding the stack 。在每一個函數(shù)被推離堆棧之前,函數(shù)的局部類的析構(gòu)會被調(diào)用

          因此一個解決辦法就是將類封裝在一個類中,這樣變成局部類,如果拋出異常也會被自動析構(gòu)當(dāng)一個異常拋出,編譯系統(tǒng)必須

          1. 檢查發(fā)生throw操作的函數(shù)
          2. 決定throw操作是否發(fā)生在try區(qū)段
          3. 若是,編譯系統(tǒng)必須把異常類型拿來和每一個catch子句進行比較
          4. 如果比較后吻合,流程控制應(yīng)該交到catch子句中
          5. 如果throw的發(fā)生并不在try區(qū)段中,或沒有一個catch子句吻合,那么系統(tǒng)必須:摧毀所有活躍局部類 從堆棧中將目前的函數(shù)(unwind)釋放掉 進行到程序堆棧的下一個函數(shù)中去,然后重復(fù)上述步驟2—5

          7.3 執(zhí)行期類型識別

          當(dāng)兩個類有繼承關(guān)系的時候,我們有轉(zhuǎn)換需求時,可以進行向下轉(zhuǎn)型,但是很多時候是不安全的C++的RTTI (執(zhí)行期類型識別)提供了一個安全的向下轉(zhuǎn)型設(shè)備,但是只對多態(tài)(繼承和動態(tài)綁定)的類型有效,其用來支持RTTI的策略就是,在C++的虛函數(shù)表的第一個slot處,放一個指針,指向保存該類的一些信息----即type_info類(在編譯器文件中可以找到其定義)dynamic_cast運算符可以在執(zhí)行期決定真正的類型對于指針來說:如果轉(zhuǎn)型成功,則會返回一個轉(zhuǎn)換后的指針如果是不安全的,會傳回0放在程序中通過 if 來判斷是否成功,采取不同措施對于引用來說:如果引用真正轉(zhuǎn)換到適當(dāng)?shù)淖宇悾瑒t可以繼續(xù)如果不能轉(zhuǎn)換的話,會拋出 bad_cast 異常通常使用 try 和 catch 來進行判斷是否成功

          Typeid運算符可以傳入一個引用,typeid運算符會傳回一個const reference,類型為type_info。其內(nèi)部已經(jīng)重載了 == 運算符,可以直接判斷兩個是否相等,回傳一個bool值例如 ?if( typeid( rt ) == typeid( fct ) )RTTI雖然只適用于多態(tài)類,但是事實上type_info object也適用于內(nèi)建類,以及非多態(tài)的使用者自定類型,只不過內(nèi)建類型 得到的type_info類是靜態(tài)取得,而不是執(zhí)行期取得。



          品味經(jīng)典


          ▊《C++ Primer中文版(第5版)

          Stanley B. Lippman,Josee Lajoie,Barbara E. Moo 著

          王剛 楊巨峰 譯?


          • 如果只讀一本C++書,本書將是你永不局悔的選擇

          • 征服全球數(shù)千萬讀者的大師之作

          • C++學(xué)習(xí)頭牌,技術(shù)影響力圖書冠軍

          真正暢行全球20年的C++入門必讀經(jīng)典,惠及數(shù)百萬高校師生啟蒙5代國產(chǎn)程序員,語言締造者與常青藤名校數(shù)版迭代的杰作,系統(tǒng)透徹:從初學(xué)到專家可全程案頭備用。

          (快快掃碼搶購吧!)

          ▊《Effective C++:改善程序與設(shè)計的55個具體做法(第三版)中文版

          Scott Meyers 著

          侯捷 譯


          • 一本輕薄短小高密度的“專家經(jīng)驗累積”

          • 國際影響力波及了整個計算機技術(shù)出版領(lǐng)域


          本書不是讀完一遍就可以束之高閣的快餐讀物,也不是用以解決手邊問題的參考手冊,而是需要您去反復(fù)閱讀體會的,C++是真正程序員的語言,背后后精神的思想與無以倫比的表達能力,這使得它具有類似宗教般的魅力。希望這本書能夠幫您跨越C++的重重險阻,領(lǐng)略高處才有的壯美風(fēng)光,做一個成功而快樂的C++程序員。

          ▊《More Effective C++:35個改善編程與設(shè)計的有效方法(中文版)

          Scott Meyers 著

          侯捷 譯

          • 梅耶爾大師Effective三部曲之一

          繼Effective C++之后,Scott Meyers于1996推出這本“續(xù)集”。條款變得比較少,頁數(shù)倒是多了一些,原因是這次選材比“第一集”更高階

          京東滿100減50快快掃碼搶購吧!)

          ▊《深度探索C++對象模型》

          Stanley,B. Lippman?著

          侯捷?譯


          • 一位偉大的C++編譯程序設(shè)計者向你闡述他如何處理各種explicit(明確出現(xiàn)于C++程序代碼中)和implicit(隱藏于程序代碼背后)的C++語意

          本書專注于C++面向?qū)ο蟪绦蛟O(shè)計的底層機制,包括結(jié)構(gòu)式語意、臨時性對象的生成、封裝、繼承,以及虛擬――虛擬函數(shù)和虛擬繼承。

          這本書讓你知道:一旦你能夠了解底層實現(xiàn)模型,你的程序代碼將獲得多么大的效率。Lippman澄清了那些關(guān)于C++額外負荷與復(fù)雜度的各種錯誤信息和迷思,但也指出其中某些成本和利益交換確實存在。他闡述了各式各樣的實現(xiàn)模型,指出它們的進化之道及其本質(zhì)因素。書中涵蓋了C++對象模型的語意暗示,并指出這個模型是如何影響你的程序的

          京東滿100減50快快掃碼搶購吧!)

          ▊《C++標準庫(第2版)

          【德】Nicolai M. Josuttis 著

          侯捷 譯


          • 全球C++經(jīng)典權(quán)威參考書

          • 1100頁鴻篇巨著,基于C++11重寫全書示例代碼


          標準庫提供了一組公共類和接口,極大地拓展了C++語言核心功能。本書詳細講解了每一標準庫組件,包括其設(shè)計目的和方法、復(fù)雜概念的剖析、實用而高效的編程細節(jié)、存在的陷阱、重要的類和函數(shù),又輔以大量用C++11標準實現(xiàn)的實用代碼范例。除覆蓋全新組件、特性外,本書一如前版,重點著眼于標準模板庫(STL),涉及容器、迭代器、函數(shù)對象以及STL算法。此外,本書同樣關(guān)注lambda表達式、基于區(qū)間的for循環(huán)、move語義及可變參數(shù)模板等標準庫中的新式C++編程風(fēng)格及其影響

          京東滿100減50快快掃碼搶購吧!)


          ▊《C++服務(wù)器開發(fā)精髓》

          張遠龍?著


          • 從操作系統(tǒng)原理角度講解C++服務(wù)器開發(fā)技術(shù)棧

          • 內(nèi)容詳盡細致、版本新

          • 重磅級C++服務(wù)器開發(fā)紅寶書


          本書詳細講解如何掌握C++服務(wù)器開發(fā)技術(shù),以及如何成為合格的C++開發(fā)者,秉承的思想是,通過掌握技術(shù)原理,可以輕松制造“輪子”,靈活設(shè)計出優(yōu)雅、魯棒的服務(wù),并快速學(xué)習(xí)新技術(shù)。

          無論是對于C/C++開發(fā)者、計算機專業(yè)的學(xué)生,還是對于想了解操作系統(tǒng)原理的讀者,本書都極具參考價值。


          掃碼了解本書詳情



          如果喜歡本文
          歡迎?在看留言分享至朋友圈?三連


          ?熱文推薦??





          瀏覽 24
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                    <th id="afajh"><progress id="afajh"></progress></th>
                    大香蕉久久久久久成人草 | 波多野结衣在线精品 | 三级片在线欧美 | 天天干免费手机视频 | 免费的黄色国产视频 |