請看下面的程式,說說會出現什麼問題?
#include
#include
#include
using namespace std;
class cdemo ;
~cdemo()
; char* str;
}; int main(int argc, char** argv)
這個程式在退出時,會出問題,什麼問題?重複delete同一片記憶體,程式崩潰。
我們把析構函式改為如下,可以更清楚的看到這一點:
~cdemo()
}; 執行時我們發現列印如下資訊:
&cdemo0=000309d8, str=000307a8
&cdemo1=0013ff70, str=000307a8
也就是說,發生了cdemo類的兩次析構,兩次析構str所指向的同一記憶體位址空間(兩次str值相同=000307a8)。
為什麼?
《程式設計師面試寶典》第二版,p99,有句解釋「vector物件指標能夠自動析構,所以不需要呼叫delete a1,否則會造成兩次析構物件」
我切以為這句話說的有點不妥。任何物件如果是通過new操作符申請了空間,必須顯示的呼叫delete來銷毀這個物件。所以「delete a1; 」這條語句是沒有錯誤的。
這句話「vector*a1=new vector(); 」定乙個指標,指向 vector,病用new操作符進行了初始化, 我們必須在適當的時候釋放a1所佔的記憶體空間,所以「delete a1; 」這句話是沒有錯誤的。另外,我們必須明白一點,釋放vector物件,vector所包含的元素也同時被釋放。
那到底那裡錯誤?
這句a1的宣告和初始化語句「vector*a1=new vector(); 」說明a1所含元素是「cdemo」型別的,在執行「a1->push_back(d1); 」這條語句時,會呼叫cdemo的拷貝建構函式,雖然cdemo類中沒有定義拷貝建構函式,但是編譯器會為cdemo類構建乙個預設的拷貝建構函式(淺拷貝),這就好像任何物件如果沒有定義建構函式,編譯器會構建乙個預設的建構函式一樣。
正是這裡出了問題。a1中的所有cdemo元素的str成員變數沒有初始化,只有乙個四位元組(32位機)指標空間。
「a1->push_back(d1);」這句話執行完後,a1裡的cdemo元素與d1是不同的物件,但是a1裡的cdemo元素的str與d1.str指向的是同一塊記憶體,這從後來的列印資訊就可以看出來。
我們知道,區域性變數,如「cdemo d1; 」 在main函式退出時,自動釋放所佔記憶體空間,
那麼會自動呼叫cdemo的析構函式「~cdeme」,問題就出在這裡。
前面的「delete a1;」已經把 d1.str 釋放了(因為a1裡的cdemo元素的str與d1.str指向的是同一塊記憶體),main函式退出時,又要釋放已經釋放掉的 d1.str 記憶體空間,所以程式最後崩潰。
解釋清楚了。
這裡最核心的問題歸根結底就是淺拷貝和深拷貝的問題。如果cdemo類新增乙個這樣的拷貝建構函式就可以解決問題:
cdemo(const cdemo &cd)
;這就是深拷貝。
或者這樣用:
vector*a1=new vector();
a1->push_back(&d1);
那麼在 「delete a1;」 a1釋放,同時a1裡面包含的元素(」cdemo*「型別,仍然是乙個指標,4位元組空間)。
JS 變數及深淺拷貝
js變數分為基本型別和引用型別 基本型別資料報括number,string,boolean,null,undefined五種型別 引用資料型別包括array,date,regexp,function等,統稱為object型別。js變數的儲存方式 基本型別變數儲存在記憶體的棧中,棧內分別儲存著變數的識...
深淺拷貝以及深淺拷貝的方法
先考慮一種情況,對乙個已知物件進行拷貝,編譯系統會自動呼叫一種建構函式 拷貝建構函式,如果使用者未定義拷貝建構函式,則會呼叫預設拷貝建構函式。執行結果 呼叫一次建構函式,呼叫兩次析構函式,兩個物件的指標成員所指記憶體相同,name指標被分配一次記憶體,但是程式結束時該記憶體卻被釋放了兩次,會造成記憶...
拷貝和深淺拷貝
當list2為list的拷貝物件時,list內的可變資料型別變化,list2變化 list內的不可變資料型別變化,list2變化。總之 list變化list2一定變化 list 1,2,3,4,list2 list print list print list2 1,2,3,4,5 1,2,3,4,5...