昨天爲了對c#的一些機制做更深的理解,特別用了c++的實現與之作對比。這不,c#沒搞出大名堂,c++卻揪出了一塊煩心的事情:
還是昨天那個代碼
原本設計SendPersonByReference函數的功能就是傳進一個對象的引用(指針和&引用都算,泛化的引用),改變該引用引用的對象。像上面那樣設計好,自我感覺還挺良好,先造個輔存tmp,記錄原對象的地址,接着析構delete,再將p指向新new的對象。一切ok!(ps:運行的機子是dell的商用機,vista sp1英文版,vs2008 英文版) 運行結果如下圖:
pdpd的回住所,突發奇想,想到再運行下代碼,於是在自己的小黑(win7 7000 英文版,vs2005)下balabala的敲好代碼再運行,ft程序直接崩潰。。。。。。
剛開始想不通,很鬱悶,但是後來仔細想想,卻發現自己在c++ 指針上犯了一個特大的煞筆級別的錯誤:踩到了“棧上動態分配堆內存”的大地雷。
地雷:p指針傳入,用p new了一塊堆內存,void的返回值。函數體一旦結束,拜拜,指針p被銷燬了,那塊可憐內存死在荒野無人問津,memory leak。
內存問題先談到這裏,就單單談功能的實現,其實我原先設計的函數是向下面這樣的(更加煞筆):
void SendPersonByReference(Person *p)
{
p->personAge = 555;
p = new Person("Nikki", 10000);
}
運行完出來結果:p指向的對象沒變….首先,我總結爲內存肯定是泄漏了,p在第二句掉頭指向新new的對象。於是我就把代碼改成了剛開始上面的代碼,一運行,哈哈,在dell機上ok,以爲萬事大吉!誰知在回家後卻發現代碼重大的隱患…
仔細分析:
1. 針對最開始的代碼,首先就是出現了棧上動態分配堆內存的大忌(詳見上面的地雷)
2. 從函數設計和功能上來說,我傳入一個對象指針p,其實也只是對指針p的拷貝,函數體中實際在做的操作其實全都是p’
p = new Person("Nikki", 10000);
這一句其實只是把p‘指向新得到的對象,而不是改變了真正需要改變的p。所以,就算你沒有memory leak,它的運行的結果也不是你原先設計想要的答案。
3. 應該如何設計呢?
void SendPersonByReference(Person **p)
{
*p = new Person("Nikki", 10000);
}
4. 最後一個疑問
爲什麼原先的設計在dell的機器上運行的時候,錯誤的設計卻得到了正確的答案,而在別的機器上運行又是錯誤的呢?
這個問題我也沒想特別的明白,windows以及編譯器內部的處理機制也不是特別的清楚,估計應該是
void SendPersonByReference(Person *p)
{
Person *tmp = p;
p->personAge = 555;
delete tmp;
// p = new Person("fds",234);
p = new Person("Nikki", 10000);
}
通過delete tmp,我們將p,p‘,tmp共同指向的內存區域“回收”了,p,p’以及tmp成爲了傳說中的野指針。(事實上它們亂指一氣嗎?) 。通過p = new Person("Nikki", 10000); p‘指向了新的內存區域,按照在dell上的運行結果反推回去,我們可以知道,新分配的內存其實就是原先被delete的內存區域,不然爲何p(不是p’)會指向新開闢的內存區域~。根據我的推斷,也就是說編譯器以及os將最新回收的內存立即又分配給了新的請求(new new Person("Nikki", 10000) ),具體vista以及編譯器的內部實現,水平太差,有待繼續深入研究。
爲了進一步的驗證我的臆測,我修改代碼:
void SendPersonByReference(Person *p)
{
Person *tmp = p;
p->personAge = 555;
delete tmp;
p = new Person("damn",1234);
p = new Person("Nikki", 10000);
}
運行結果和前面的運行結果一樣,也就是說在函數棧沒被destroy之前,p指向了“new Person("damn",1234);”的地址,p‘指向“p = new Person("Nikki", 10000);”指向的地址。當然了,函數體結束以後,遺漏了2塊堆內存,一塊(new Person("Nikki", 10000);)真的是泄露了,另一塊(p = new Person("damn",1234);)歪打正着被原先的實參p指着。
總結:
依靠野指針未確定的行爲進行編程是極其有害和邪惡的,我們當然應該避之不及。C++給了程序員足夠的信任和自由去操作內存和系統,但也另外給了我們一顆顆隱患的地雷,繼續修煉吧devx!以上有錯誤的地方的,還肯請大牛小牛們指點,俺現在水平就這麼點,想到的全寫了。止筆於此,該看c#去了…….