網上比較經典的總結
(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中常見。
本文轉自:http://blog.csdn.net/wind19/article/details/8213114