构造函数失败_抛出异常
網上比較經典的總結:
什么函數都有可能失敗,構造函數也不另外,比如new一個對象或空間不成功。當構造函數失敗的時候,其實很多時候我們不想這個對象被繼續生成,這個時候就可以在構造函數里面拋出異常。C++規定構造函數拋出異常之后,對象將不被創建,析構函數也不會被執行,但已經創建成功的部分(比如一個類成員變量)會被部分逆序析構,不會產生內存泄漏。但有些資源需要在拋出異常前自己清理掉,比如打開成功的一個文件,最好關閉掉再拋出異常(雖然系統也會把這個資源回收),因為拋出異常之后析構函數不會被執行了。
? ? ? (1) C++中通知對象構造失敗的唯一方法那就是在構造函數中拋出異常;(這句話并不是說我們只有這個方法才能讓上層知道構造函數失敗,雖然構造函數沒有返回值,我們完全可以在構造函數中傳入一個引用值,然后在里面設置狀態,運行完構造函數之后任然可以知道是否失敗,但這種情況下面對象其實還是被構造出來的,只是里面有資源分配失敗而已,并且析構函數還是會執行。這和我們構造失敗不生成對象的初衷不符。)
(2) 構造函數中拋出異常將導致對象的析構函數不被執行;(但已經生產的部分成員變量還是會被逆向析構的)
(3) 當對象發生部分構造時,已經構造完畢的子對象將會逆序地被析構;
?
網上的一個栗子:
一個實例對象的構造:
第一步,分配足夠的內存,如果失敗就是棧溢出或拋出std::bad_alloc的異常,所以在這步你不用擔心內存泄露,而且這一步你是不能插手的,如果這步成功,就進入第二步。
new運算符的實現保證了內存泄漏不會發生。例如
T *p =?new?T;
將被編譯器轉換給類似下面的樣子:(其實和我們自己釋放已經申請的資源的思想流程是一樣的)
// 第一步,分配原始內存,若失敗則拋出bad_alloc異常 try {// 第二步,調用構造函數構造對象new (p)T; // placement new: 只調用T的構造函數 } catch(...) {delete []p; // 釋放第一步分配的內存throw; // 重拋異常,通知應用程序 } 第二步,調用構造函數,在通常情況下,如果構造函數為空或沒有進行動態內存分配,你就不用關心內存泄露了你需要關心的是構造函數中有動態內存分配
class A {char* str[10]; public:A(){for(int i=0;i<10;i++)str[i]=NULL; //對str[]初始化,這是必須的,不然再后面delete就會出現問題try{for(int i=0;i<10;i++)str[i]=new char[1024*1024*1024]; //要來就來狠的 }catch(bad_alloc){for(int i=0;i<10;i++)delete []str[i]; //放心,即使delete NULL是不會出問題的throw; //就拋出這個bad_alloc, 這才是構造函數拋出去的異常,外層會撲捉到,并且析構函數不會被調用 }}~A(){for(int i=0;i<10;i++)delete []str[i]; } }; int main() {A *pA=NULL;try{pA=new A;}catch(bad_alloc){cout<<"Out of memory"<<endl;}delete pA;return 0; } pA是用NULL初始化的,即使在給A分配內存時(第一步)失敗,也不會導致后面的delete?pA出錯。
?
對于構造函數可能失敗的做法一般有兩種
1. 在構造函數中拋出異常,本對象構造未完成,它的析構函數不會被調用。當然,我們有義務釋放已經分配到的資源。簡單,最常見。
2. 把資源的初始化工作放在另一個單獨函數中,比如?bool?init(...),由對象創建者(比如工廠方法)先調用構造函數,再調用init方法。ATL中常見。
轉載于:https://www.cnblogs.com/Lunais/p/5674123.html
總結
以上是生活随笔為你收集整理的构造函数失败_抛出异常的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 设计模式之笔记--简单工厂模式(Simp
- 下一篇: Linux(Ubuntu 14.0)