類繼承中建構函式和解構函式地調用順序

來源:互聯網
上載者:User
繼承 建構函式 解構函式

  

類繼承中建構函式和解構函式的調用

現在,有三個類,類的定義如下

class CA
{
public:
 CA(){cout<<"CA constructor"<<endl;}
 
    ~CA(){cout<<"CA desstructor"<<endl;}

};

class CB:public CA
{
public:
 CB(){cout<<"CB constructor"<<endl;}
 
 ~CB(){cout<<"CB desstructor"<<endl;}
};

class CC:public CB
{
public:
 CC(){cout<<"CC constructor"<<endl;}
 
 ~CC(){cout<<"CC desstructor"<<endl;}
};

CA是爺爺,CB是爸爸,CC是兒子。

那麼任何一本C++的書都會講,建構函式的調用順序是CA CB CC,解構函式的調用順序是CC,CB,CA,什嗎???你的書沒講,靠,扔了吧

於是

(1) int main()
{
   CC p ;
}
這個程式運行結果是
CA constructor
CB constructor
CC constructor

CC desstructor
CB desstructor
CA desstructor

靠,太簡單了,一個雞蛋飛過來了,:(

繼續……………………

(2) 再做第二個實驗之前,先做一點小小修改
~CA(){cout<<"CA desstructor"<<endl;}  ----->>>
virtual  ~CA(){cout<<"CA desstructor"<<endl;}

修改main 代碼如下
int main()
{
   CA * p = new CC();

   delete p;

   return 0;
}

yeah
結果一模一樣哦
CA constructor
CB constructor
CC constructor

CC desstructor
CB desstructor
CA desstructor

但是如果把virtual  ~CA(){cout<<"CA desstructor"<<endl;}
的virtual 去掉

那麼(2)中的運行結果為
CA constructor
CB constructor
CC constructor

CA desstructor

只調了CA的哦,出問題了
這樣的話,就會出現基類的建構函式調用了,但是衍生類別的建構函式沒調用,
對象的派生部分不會被銷毀,這將導致資源泄漏

所以我們在設計一個類的時候,如果類至少擁有一個虛函數,或者說基類被設計用於多態,在這種情況下,
一個衍生類別的對象可能通過一個基類指標來進行操作,然後進行銷毀,如果這樣的話,那麼這個基類的解構函式要設定成虛擬,
有些類雖然是基類,但是不是用於多態的,沒有虛函數,沒有被設計成允許經由基類介面衍生類別對象進行操作,那麼也無需設成虛解構函式,畢竟增加了開銷,
好了,解釋清楚了,我們也知道怎麼做了,繼續實驗

(3)
保留CA中的虛解構函式

修改main 代碼如下
int main()
{
   CB * p = new CC();

   delete p;

   return 0;
}

運行結果
CA constructor
CB constructor
CC constructor

CC desstructor
CB desstructor
CA desstructor

取消CA中的虛解構函式,那麼,CA,CB,CC中沒有虛解構函式
那麼3中代碼運行結果如下

CA constructor
CB constructor
CC constructor

CB desstructor
CA desstructor

只調到CB的析構哦,

繼續實驗,CA,CB,CC中,只有CB是虛解構函式
3中代碼運行如下

CA constructor
CB constructor
CC constructor

CC desstructor
CB desstructor
CA desstructor

所以,如果是CB指向衍生類別,只要CB或者其基類中存在虛解構函式,那麼也是所有的解構函式都調用的了

繼續………………

(4)
修改main 代碼如下
int main()
{
   CA * p = new CC();

   delete p;

   return 0;
}

如果A的解構函式是虛的,那麼情況如2,不多說了

如果是CA的解構函式不是虛的,而CB或者CC的解構函式是虛擬,那麼在調用delete p;會出現記憶體錯誤

Expression:_BLOCK_TYPE_IS_VALID(pHead->nBlockUse)  
是在釋放記憶體的時候出現這樣的錯誤
上網查了一下,_BLOCK_TYPE_IS_VALID是用來檢測記憶體有效性宏中的一個,這個錯誤說明指標使用出現了問題

後來想了一想,應該是因為繼承類中出現了虛函數,所以多了一個指向虛函數表的指標,而基類中一個虛函數都沒有,所以也沒有這個指標啦
所以在delete的時候就出現了記憶體錯,事實證明,這個猜想應該是站得住腳的,在CA中添加一個虛函數,即使的空的虛函數,也不會出現記憶體錯,
關於這個問題,我想在下次繼續討論吧,這裡不深入進去了,
回到正題,在CA中添加一個空的,任意的虛擬函數以後,運行正確了,
運行結果是
CA constructor
CB constructor
CC constructor

CA desstructor

這與(2)中的情況是一樣的,只要CA的解構函式不是虛擬,就只能調用CA的析構了

最後來看一種非常Bt的做法
(5)
CA CB CC中的解構函式,誰是虛擬,無所謂,隨便

修改main 代碼如下
int main()
{
   void * p = new CC();

   delete p;

   return 0;
}

運行結果
CA constructor
CB constructor
CC constructor

下面呢???下面沒有了,暈……………………

這種情況,構造了一個cc的對象,然後呢,沒有調解構函式,直接把申請的記憶體釋放了,最好不要這樣用咯

好了,最後,上面,基本上把所有的,我能想到的情況都整理了一下,
總結一下:


C1 * p = new C2();
delete p;
這樣的代碼

這裡,C1是C2的基類,C1可能是C2的爸爸,可能是爺爺,可能是爸爸的爺爺,可能是爺爺的爺爺…………………………

那麼首先,調用的建構函式是
從C2的第一個祖先一直到C2………………。和C1是什麼沒關係

在delete p的時候,那麼有以下幾種情況:
1) C1或者C1的祖先(基類)中,含有虛解構函式,那麼調用的解構函式的順序是從C2一直到C2的第一個祖先(#add最開始的祖先)
2)如果C1或者C1的祖先中,沒有一個是類是含有虛解構函式的,那麼調用的是從C1一直到C1的(也是C2的)第一個祖先的(#add表述有待細思)
3)如果C1是void,那就什麼析構都不調用了。

 

如果一個類,作為多態的基類,那麼盡量把解構函式聲明成虛擬,不然………………,

 

好了,就此打住吧,關於4中的記憶體錯誤,下次再談論吧,項目要開始了,又要忙了,哭去了……………………

參考書籍《Effective 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.