C++中的new/delete、構(gòu)造/析構(gòu)函數(shù)、dynamic_cast分析
1,new 關(guān)鍵字和 malloc 函數(shù)區(qū)別(自己、功能、應(yīng)用):
1,new 關(guān)鍵字是 C++ 的一部分:
1,如果是 C++ 編譯器,則肯定可以用 new 申請堆空間內(nèi)存;
2,malloc 是由 C 庫提供的函數(shù):
1,如果沒有相應(yīng)的庫,malloc 將不能使用;
2,有些特殊的嵌入式開發(fā)中,少了 C 庫,則就不能動(dòng)態(tài)內(nèi)存分配;
3,new 以具體類型為單位進(jìn)行內(nèi)存分配;
1,面向?qū)ο笾幸话阌?new,不用 malloc;
4,malloc 以字節(jié)為單位進(jìn)行內(nèi)存分配;
5,new 在申請內(nèi)存空間時(shí)可進(jìn)行初始化;
1,觸發(fā)構(gòu)造函數(shù)調(diào)用;
6,malloc 僅根據(jù)需要申請定量的內(nèi)存空間;
1,對象的創(chuàng)建只能用 new,malloc 不適合面向?qū)ο箝_發(fā);
2,下面代碼輸出什么?為什么?見 new 和 malloc 的區(qū)別編程實(shí)驗(yàn):
#include <iostream> #include <string> #include <cstdlib> using namespace std; class Test { int* mp; //為了說明 free() 可能造成內(nèi)存泄漏問題而添加的成員變量; public: Test() { cout << "Test::Test()" << endl; mp = new int(100); // 申請 4 個(gè)字節(jié)堆空間并初始化為 100; cout << *mp << endl; } ~Test() { delete mp; // 析構(gòu)函數(shù)歸還堆空間;但是如果僅僅用 free() 函數(shù)歸還堆空間,這里析構(gòu)函數(shù)沒有調(diào)用,則對象沒有摧毀,那么就造成了堆空間泄漏,這在大型項(xiàng)目開發(fā)中是不可原諒的; cout << "~Test::Test()" << endl; } }; int main() { Test* pn = new Test; // 第一步申請堆空間,第二步(申請成功后)在堆空間上調(diào)用構(gòu)造函數(shù)、因?yàn)樾枰跏蓟? Test* pm = (Test*)malloc(sizeof(Test)); // 這行代碼運(yùn)行完后,pm 并沒有指向合法的對象,它僅僅指向一片內(nèi)存空間而已,這個(gè)時(shí)候這片內(nèi)存空間不能夠成為一片合法的對象,因?yàn)榫蜎]有對象; delete pn; // 動(dòng)態(tài)歸還堆空間;第一步 delete 觸發(fā)析構(gòu)函數(shù)調(diào)用,摧毀對象,第二步歸還堆空間;在歸還堆空間的時(shí)候,要先摧毀掉對象,否則容易出現(xiàn)內(nèi)存泄漏; free(pm); // 動(dòng)態(tài)規(guī)劃堆空間;僅歸還堆空間,不觸發(fā)析構(gòu)函數(shù)調(diào)用;這里不能用 delete pm,因?yàn)檫@樣會(huì)對非法對象調(diào)用構(gòu)造函數(shù),而對于析構(gòu)函數(shù)中的 delete mp 來說,這樣的影響是深遠(yuǎn)的,不知道什么時(shí)候就會(huì)帶來 bug,且不可調(diào)試,只能通過“代碼走查”的方式來檢查是不是混用了兩種類型的申請釋放堆空間函數(shù); return 0; }
1,結(jié)論:
1,free() 可以釋放由 new 申請來的堆空間,但是 free() 不會(huì)進(jìn)行析構(gòu)函數(shù)的調(diào)用,因此有可能造成內(nèi)存泄漏;
2,new 和 delete,malloc 和 free 只能匹配使用,不能混用;
3,new 和 malloc 的區(qū)別(自己、功能、應(yīng)用):
1,new 在所有 C++ 編譯器中都被支持;
2,malloc 在某些系統(tǒng)開發(fā)中是不能調(diào)用的;
3,new 能夠觸發(fā)構(gòu)造函數(shù)的調(diào)用;
4,malloc 僅分配需要的內(nèi)存空間;
5,對象的創(chuàng)建只能使用 new;
6,malloc 不適合面向?qū)ο箝_發(fā);
4,下面的代碼輸出什么?為什么?
1,代碼示例:
int main() { Test* pn = new Test; // 調(diào)用構(gòu)造函數(shù); test* pm = (Test*)malloc(sizeof(Test)); // 僅申請堆空間; delete pn; // 調(diào)用析構(gòu)函數(shù); free(pm); // 僅釋放堆空間; return 0; }
5,delete 和 free 的區(qū)別(自己、功能、應(yīng)用):
1,delete 在所有 C++ 編譯器中都被支持;
2,free 在某些系統(tǒng)開發(fā)中是不能調(diào)用;
3,delete 能夠觸發(fā)析構(gòu)函數(shù)的調(diào)用;
4,free 僅歸還之前分配的內(nèi)存空間;
5,對象的銷毀只能使用 delete;
6,free 不適合面向?qū)ο箝_發(fā)。
6,構(gòu)造函數(shù)是否可以成為虛函數(shù)?析構(gòu)函數(shù)是否可以成為虛函數(shù)?
7,構(gòu)造函數(shù)不可能成為虛函數(shù):
1,在構(gòu)造函數(shù)執(zhí)行結(jié)束后,虛函數(shù)表指針才會(huì)被正確的初始化;
1,C++ 里面的多態(tài)是通過虛函數(shù)表和指向虛函數(shù)表指針完成的,虛函數(shù)表指針是由編譯器創(chuàng)建的,同時(shí)也是由編譯器進(jìn)行初始化,在構(gòu)造函數(shù)執(zhí)行結(jié)束之后,虛函數(shù)表的指針才會(huì)被正確進(jìn)行初始化;
2,在構(gòu)造函數(shù)執(zhí)行的過程當(dāng)中,虛函數(shù)表的指針有可能是沒有被正確初始化的,因?yàn)閷τ谔摵瘮?shù)表和虛函數(shù)表指針的實(shí)現(xiàn),對于不同的 C++ 編譯器而言,實(shí)現(xiàn)有可能不一樣,但是所有的 C++ 編譯器都會(huì)保證在構(gòu)造函數(shù)執(zhí)行結(jié)束后,虛函數(shù)表指針肯定會(huì)被正確的初始化,在這之前,是沒有保證的;
3,所以構(gòu)造函數(shù)不可能成為虛函數(shù),創(chuàng)建一個(gè)對象的時(shí)候,我們需要構(gòu)造函數(shù)來初始化虛函數(shù)表的指針,因此構(gòu)造函數(shù)相當(dāng)于一個(gè)入口點(diǎn),這個(gè)入口點(diǎn)負(fù)責(zé)虛函數(shù)調(diào)用的前期工作,這個(gè)入口點(diǎn)當(dāng)然不可能是虛函數(shù);
8,析構(gòu)函數(shù)可以成為虛函數(shù):
1,析構(gòu)函數(shù)在對象銷毀之前被調(diào)用,對象銷毀之前意味著虛函數(shù)指針是正確的指向?qū)?yīng)的虛函數(shù)表的;
2,建議在設(shè)計(jì)類時(shí)將析構(gòu)函數(shù)聲明為虛函數(shù)(工程中設(shè)計(jì)一個(gè)父類的析構(gòu)函數(shù)為虛函數(shù));
1,賦值兼容性申請子類對象給父類指針時(shí),當(dāng) delete 作用在指針上時(shí),編譯器會(huì)直接根據(jù)指針類型(此時(shí)是父類)來調(diào)用相應(yīng)的析構(gòu)函數(shù),若父類加上 virtual,編譯器可以根據(jù)指針指向的實(shí)際對象(此時(shí)是子類)決定如何調(diào)用析構(gòu)函數(shù)(多態(tài));
9,構(gòu)造、析構(gòu)、虛函數(shù)編程實(shí)驗(yàn):
#include <iostream> #include <string> using namespace std; class Base { public: Base() // 若申請為析構(gòu)函數(shù),則編譯器在此處顯示:error:constructors cannot be declared virtual. { cout << "Base()" << endl; } virtual void func() { cout << "Base::func()" << endl; } virtual ~Base() // 申請為虛函數(shù)時(shí),編譯器無顯示 { cout << "~Base()" << endl; } }; class Derived : public Base { public: Derived() { cout << "Derived()" << endl; } virtual void func() { cout << "Derived::func()" << endl; } ~Derived() { cout << "~Derived()" << endl; } }; int main() { Base* p = new Derived(); // ... delete p; // 期望調(diào)用完子類析構(gòu)函數(shù)再調(diào)用父類的析構(gòu)函數(shù);但是如果父類沒有申請為析構(gòu)函數(shù),則只調(diào)用父類析構(gòu)函數(shù);這是因?yàn)榇藭r(shí)刪除的是一個(gè)父類的指針,由于并沒有將析構(gòu)函數(shù)申請為 virtual,因此在這樣情況下,編譯器直接根據(jù)指針 p 的類型來決定調(diào)用哪一個(gè)構(gòu)造函數(shù),由于指針 p 的類型是父類的類型,所以編譯器直接暴力認(rèn)為調(diào)用父類構(gòu)造函數(shù)就可以了;當(dāng)將父類的虛函數(shù)聲明為 virtual 時(shí),編譯器就不會(huì)簡單的根據(jù)指針 p 的類型來簡單調(diào)用父類的或者是子類的析構(gòu)函數(shù)了,這個(gè)時(shí)候由于析構(gòu)函數(shù)是虛函數(shù),所以在執(zhí)行這行代碼的時(shí)候,編譯器會(huì)根據(jù)指針 p 指向的實(shí)際對象來決定如何調(diào)用析構(gòu)函數(shù),這是多態(tài); return 0; }
1,工程中設(shè)計(jì)一個(gè)類作為父類出現(xiàn)時(shí),我們都要將析構(gòu)函數(shù)聲明為虛函數(shù),否 則就有可能產(chǎn)生內(nèi)存泄漏,因?yàn)橛锌赡芴^子類析構(gòu)函數(shù)的調(diào)用,如果子類 析構(gòu)函數(shù)中有釋放資源的操作(動(dòng)態(tài)內(nèi)存空間),則后果不堪設(shè)想;
10,構(gòu)造函數(shù)中是否可以發(fā)生多態(tài)?析構(gòu)函數(shù)中是否可以發(fā)生多態(tài)?
11,構(gòu)造函數(shù)中(構(gòu)造函數(shù)中調(diào)用虛函數(shù))不可能發(fā)生多態(tài)行為:
1,在構(gòu)造函數(shù)執(zhí)行時(shí),虛函數(shù)表指針未被正確初始化;
12,析構(gòu)函數(shù)中(析構(gòu)函數(shù)中調(diào)用虛函數(shù))不可能發(fā)生多態(tài)行為:
1,在析構(gòu)函數(shù)執(zhí)行時(shí),虛函數(shù)表指針可能已經(jīng)被摧毀;
13,析構(gòu)函數(shù)和構(gòu)造函數(shù)中(調(diào)用虛函數(shù)時(shí))不能發(fā)生多態(tài)行為,只調(diào)用當(dāng)前類中的函數(shù)版本;
1,構(gòu)造函數(shù)和析構(gòu)函數(shù)中調(diào)用虛函數(shù)實(shí)驗(yàn):
#include <iostream> #include <string> using namespace std; class Base { public: Base() { cout << "Base()" << endl; func(); } virtual void func() { cout << "Base::func()" << endl; } virtual ~Base() { func(); cout << "~Base()" << endl; } }; class Derived : public Base { public: Derived() { cout << "Derived()" << endl; func(); } virtual void func() { cout << "Derived::func()" << endl; } ~Derived() { func(); cout << "~Derived()" << endl; } }; int main() { Base* p = new Derived(); // 打印 Base(),Base::func(),Derived(),Derived::func(), // ... delete p; // 打印 Derived::func(),~Derived(),Base::func(), ~Base(); return 0; }
14,繼承中如何正確的使用強(qiáng)制類型轉(zhuǎn)換?
1,dynamic_cast 是與繼承相關(guān)的類型轉(zhuǎn)換關(guān)鍵字;
2,dynamic_cast 要求相關(guān)的類中必須有虛函數(shù);
3,用于有直接或者間接繼承關(guān)系的指針(引用)之間;
1,指針:
1,轉(zhuǎn)換成功:得到目標(biāo)類型的指針;
2,轉(zhuǎn)換失?。旱玫揭粋€(gè)空指針;
2,引用:
1,轉(zhuǎn)換成功:得到目標(biāo)類型的引用;
2,轉(zhuǎn)換失?。旱玫揭粋€(gè)異常操作信息;
4,編譯器會(huì)檢查 dynamic_cast 的使用是否正確;
1,在 C++ 編譯器中得到足夠重視,是非常有地位的一個(gè)類型轉(zhuǎn)換關(guān)鍵字;
2,使用不正確編譯器會(huì)報(bào)錯(cuò);
5,類型轉(zhuǎn)換的結(jié)果只可能在運(yùn)行階段才能得到;
1,動(dòng)態(tài)的類型轉(zhuǎn)換,轉(zhuǎn)換結(jié)果運(yùn)行階段才能得到;
15,dynamic_cast 的使用編程實(shí)驗(yàn):
#include <iostream> #include <string> using namespace std; class Base { public: Base() { cout << "Base::Base()" << endl; } virtual ~Base() // 工程經(jīng)驗(yàn); { cout << "Base::~Base()" << endl; } }; class Derived : public Base { }; int main() { /* Base* p = new Derived; Derived* pd = p; // 編譯器顯示:error:invalid conversion from 'Base*' to 'Derived*'; // 未有虛函數(shù)時(shí),用 dynamic_cast 轉(zhuǎn)換,編譯器顯示:error: cannot dynamic_cast 'p' (of type 'class Base*') to type 'Derived*' (source type is not polymorphic(多態(tài)的)) // 有虛函數(shù)且用了 dynamic_cast 也要判斷 pd 不為空; */ Base* p = new Base; Derived* pd = dynamic_cast<Derived*>(p); // 不合法,不能使用子類指針,指向父類對象;編譯器編譯階不報(bào)錯(cuò);但是運(yùn)行時(shí) pd = 0; 意味著此處強(qiáng)制類型轉(zhuǎn)換不成功; if( pd != NULL ) // 這樣的判斷很有必要; { cout << "pd = " << pd << endl; } else { cout << "Cast error!" << endl; } delete p; return 0; }
總結(jié)
以上所述是小編給大家介紹的C++中的new/delete、構(gòu)造/析構(gòu)函數(shù)、dynamic_cast分析,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
如果你覺得本文對你有幫助,歡迎轉(zhuǎn)載,煩請注明出處,謝謝!
- C++強(qiáng)制類型轉(zhuǎn)換(static_cast、dynamic_cast、const_cast、reinterpret_cast)
- 淺析C++中dynamic_cast和static_cast實(shí)例語法詳解
- 由static_cast和dynamic_cast到C++對象占用內(nèi)存的全面分析
- C++中的類型轉(zhuǎn)換static_cast、dynamic_cast、const_cast和reinterpret_cast總結(jié)
- c++ dynamic_cast與static_cast使用方法示例
- C++ 中dynamic_cast<>的使用方法小結(jié)
- C++的dynamic示例代碼詳解
相關(guān)文章
仿現(xiàn)代C++智能指針實(shí)現(xiàn)引用計(jì)數(shù)
這篇文章主要為大家詳細(xì)介紹了如何仿現(xiàn)代C++智能指針實(shí)現(xiàn)引用計(jì)數(shù),文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,有需要的小伙伴可以了解下2024-03-03C語言當(dāng)函數(shù)執(zhí)行成功時(shí)return1還是0
本文主要介紹了C語言當(dāng)函數(shù)執(zhí)行成功時(shí)return1還是0,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09c++ builder TreeView控件節(jié)點(diǎn)遍歷代碼
這篇文章介紹了c++ builder TreeView控件節(jié)點(diǎn)遍歷代碼,有需要的朋友可以參考一下2013-09-09C++?opencv圖像處理實(shí)現(xiàn)灰度變換示例
這篇文章主要為大家介紹了C++?opencv圖像處理灰度變換的實(shí)現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05C/C++中的atan和atan2函數(shù)實(shí)例用法
在本篇文章里小編給大家分享的是一篇關(guān)于C/C++中的atan和atan2函數(shù)實(shí)例用法相關(guān)內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。2020-02-02位運(yùn)算實(shí)現(xiàn)十進(jìn)制轉(zhuǎn)換為二進(jìn)制
這篇文章主要介紹了位運(yùn)算實(shí)現(xiàn)十進(jìn)制轉(zhuǎn)換為二進(jìn)制的相關(guān)資料,需要的朋友可以參考下2015-03-03

Java?C++?算法題解leetcode669修剪二叉搜索樹示例