<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++ | 有趣的動(dòng)態(tài)轉(zhuǎn)換之 delete 崩潰探究兼談基類(lèi)虛析構(gòu)的重要性

          共 1770字,需瀏覽 4分鐘

           ·

          2020-09-21 19:38

          前言

          在《有趣的動(dòng)態(tài)轉(zhuǎn)換》 這篇文章中,運(yùn)行 測(cè)試代碼3 會(huì)崩潰。本文試圖揭示崩潰的原因。

          錯(cuò)誤更正

          在開(kāi)始之前,需要更正《C++ 虛函數(shù)簡(jiǎn)介》中的一個(gè)錯(cuò)誤。關(guān)于 CBaseCDerived 的虛表內(nèi)容,析構(gòu)函數(shù)的位置并不是直接存儲(chǔ)了虛函數(shù)的地址,而是存儲(chǔ)了一段編譯器生成的函數(shù),該函數(shù)內(nèi)部會(huì)調(diào)用對(duì)應(yīng)的析構(gòu)函數(shù)。

          所以正確的虛表應(yīng)該是下面這樣的:

          注意:debug 版默認(rèn)會(huì)引入另外一層間接層,而 release ?版不會(huì)。

          錯(cuò)誤回顧

          回顧一下 測(cè)試代碼3 運(yùn)行后的錯(cuò)誤提示,如下圖:

          這是一個(gè)棧平衡被破壞的錯(cuò)誤。在 vs 中單步調(diào)試可以知道是在執(zhí)行 delete(pBaseA); 的時(shí)候?qū)е碌腻e(cuò)誤。奇怪的是,在崩潰之前,還輸出了一個(gè) NewB::PerfectFunctionName。光看源碼,看不出什么問(wèn)題了,需要查看反匯編代碼了。

          delete 的反匯編代碼

          根據(jù)上圖中的解釋?zhuān)瑘?zhí)行 delete (pBaseA); 會(huì)輸出 NewB::PerfectFunctionName 已經(jīng)很清楚了。但是為什么會(huì)崩潰呢?不知道有沒(méi)有小伙伴兒注意到那個(gè)奇怪的 push 1。函數(shù) NewB::PerfectFunctionName() 是沒(méi)有參數(shù)的,而這里的 push 1 卻向棧上壓入了一個(gè)參數(shù),所以棧就不平衡了。

          至此,執(zhí)行 delete (pBaseA); 會(huì)輸出 NewB::PerfectFunctionName 并且崩潰的來(lái)龍去脈應(yīng)該已經(jīng)清楚了。但是那個(gè) push 1 到底是什么呢?

          奇怪的 push 1

          為了弄清這個(gè) push 1 的來(lái)歷與作用,我把 delete pBaseA 改成了 delete((BaseB*)pBaseA);,這樣代碼會(huì)按正常的邏輯執(zhí)行。也就是會(huì)執(zhí)行到 NewB::'vector deleting destructor'。查看對(duì)應(yīng)的反匯編代碼,如下圖:

          從圖中高亮的三句反匯編語(yǔ)句可知:NewB::vector deleting destructor 需要一個(gè)參數(shù)。該參數(shù)是一個(gè)標(biāo)記,如果為 1,則調(diào)用 operator delete 釋放內(nèi)存,否則不釋放內(nèi)存。

          從整個(gè)反匯編代碼可知,NewB::vector deleting destructor 會(huì)先執(zhí)行 NewB::~NewB(),然后根據(jù)外部傳入的標(biāo)記來(lái)決定是否調(diào)用 operator delete 釋放內(nèi)存。

          至此,理清了 push 1 的用途,那什么時(shí)候會(huì) push 0 呢?

          不知道有沒(méi)有小伙伴兒顯式調(diào)用過(guò)析構(gòu)函數(shù),像下面這樣。

          如果查看 pBaseB->~BaseB() 的反匯編代碼,一切都會(huì)真相大白。如下圖:

          為什么多態(tài)基類(lèi)的析構(gòu)函數(shù)要是虛的?

          相信有經(jīng)驗(yàn)的 C++ 開(kāi)發(fā)人員一定聽(tīng)過(guò)類(lèi)似的忠告:帶有多態(tài)性質(zhì)的基類(lèi)應(yīng)該聲明一個(gè)虛析構(gòu)函數(shù)。如果類(lèi)帶有任何虛函數(shù),它就該擁有一個(gè)虛析構(gòu)函數(shù)。

          如果析構(gòu)函數(shù)不是虛函數(shù)呢?會(huì)有什么問(wèn)題嗎?稍微改動(dòng)一下測(cè)試代碼,如下:

          運(yùn)行結(jié)果如下圖:

          只有基類(lèi)的析構(gòu)函數(shù)被調(diào)用,子類(lèi)的析構(gòu)函數(shù)并沒(méi)有被調(diào)用!為什么會(huì)這樣呢?真相就在反匯編代碼里:

          從上圖可知,如果要 delete 的類(lèi)型的析構(gòu)函數(shù)是非虛的,那么 vs 中帶的編譯器在生成匯編代碼時(shí),會(huì)直接調(diào)用對(duì)應(yīng)類(lèi)型的 scalar deleting destructor,不存在多態(tài)行為!這會(huì)導(dǎo)致子類(lèi)的析構(gòu)函數(shù)沒(méi)有被調(diào)用!

          總結(jié)

          • 如果一個(gè)類(lèi)會(huì)被當(dāng)成基類(lèi)使用,請(qǐng)確保其析構(gòu)函數(shù)是虛函數(shù)。

          • 在生成 delete (pBaseA); 這條語(yǔ)句的匯編代碼時(shí),編譯器是根據(jù) pBaseA 的靜態(tài)類(lèi)型確定虛析構(gòu)函數(shù)在虛表中的位置的。而不是根據(jù) pBaseA 實(shí)際指向的類(lèi)型。

          • delete pBaseA 會(huì)先執(zhí)行 pBaseA 指向的類(lèi)型的析構(gòu)函數(shù),然后再調(diào)用 operator delete 釋放對(duì)應(yīng)的內(nèi)存。

          • 可以顯式調(diào)用一個(gè)類(lèi)的析構(gòu)函數(shù)。當(dāng)然,析構(gòu)函數(shù)的訪問(wèn)級(jí)別必須是 public 的。

          參考資料

          • vs 反匯編代碼

          • 《effective c++》


          瀏覽 75
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          C++核心準(zhǔn)則C.127:包含虛函數(shù)的類(lèi)應(yīng)該有虛析構(gòu)函數(shù)或保護(hù)析構(gòu)函...
          C++核心準(zhǔn)則C.35:基類(lèi)的析構(gòu)函數(shù)要么是公開(kāi)的虛函數(shù),要么是保護(hù)...
          <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>
                  久久久成人高清视频 | 大尺度视频在线 | 日韩一区二区在线视频 | 特级西西人体大胆无码 | 先锋影音麻豆 |