Trend科技的一道面試題: #include <cstdlib> #include <vector> using namespace std; class CDemo { public: CDemo():str(NULL){}; ~CDemo() { if(str) delete[] str; }; char* str; }; int main(int argc, char** argv) { CDemo d1; d1.str=new char[32]; strcpy(d1.str, "trend micro"); vector<CDemo> *a1=new vector<CDemo>(); a1->push_back(d1); delete a1; return EXIT_SUCCESS; } { if(str) { static int i=0; cout<<"&CDemo"<<i++<<"="<<(int*)this<<", str="<<(int *)str<<endl; delete[] str; } }; “vector<CDemo> *a1=new vector<CDemo>(); ” 說(shuō)明a1所含元素是“CDemo”類(lèi)型的,在執行“a1->push_back(d1); ”這條語(yǔ)句時(shí),會(huì )調用CDemo的拷貝構造函數,雖然CDemo類(lèi)中沒(méi)有定義拷貝構造函數,但是編譯器會(huì )為CDemo類(lèi)構建一個(gè)默認的拷貝構造函數(淺拷貝),這就好像任何對象如果沒(méi)有定義構造函數,編譯器會(huì )構建一個(gè)默認的構造函數一樣。 { this->str = new char[strlen(cd.str)+1]; strcpy(str,cd.str); }; //這就是深拷貝 。 vector<CDemo*> *a1=new vector<CDemo*>(); a1->push_back(&d1); 下面的函數是驗證::: #include <iostream> #include <cstdlib> #include <vector> using namespace std; class CDemo { public: CDemo():str(NULL) { cout <<"構造函數"<< endl; }; CDemo(const CDemo &cd)//這就是深拷貝 { cout <<"深拷貝構造函數begin!!!"<<endl; this->str = new char[strlen(cd.str)+1]; strcpy(str,cd.str); cout <<"深拷貝構造函數 end endend end !!!@@@"<<endl; }; ~CDemo() { if(str) { static int i=0; cout<<"析構函數:"<<"&CDemo"<<i++<<"="<<(int*)this<<", str="<<(int *)str<<endl; delete[] str; } }; char* str; }; int main(int argc, char** argv) { int k =1; cout<<"構造CDemo d1; begin!!!"<<endl; CDemo d1; cout<<"d1構造函數對應的值:"<<"&CDemod1="<<(int*)&d1<<", "<<endl; cout<<"構造CDemo d1; end end end end end end end end!!!!!!@@@"<<endl<<endl<<endl; d1.str=new char[32]; strcpy(d1.str, "trend micro"); cout<<"構造vector<CDemo*> *a1; begin!!!"<<endl; vector<CDemo*> *a1=new vector<CDemo*>(); cout<<"構造vector<CDemo*> *a1; end end end end end end!!!!!!@@@!!!"<<endl<<endl<<endl; cout<<"a1->push_back(d1);begin"<<endl; a1->push_back(&d1); cout<<"a1->push_back(d1);end end end end!!!!@@@@@@!!!"<<endl<<endl<<endl; cout<<"析構 a1; begin!!!"<<endl; cout<<"a1的元素d1對應的值:"<<"&CDemod1="<<(int*)a1->at(0)<<", "<<endl; delete a1; cout<<"析構 a1; end end end end end end end endend!!!!@@@@@@"<<endl<<endl<<endl; // if (k) // { // cout<<"構造CDemo d2; begin!!!"<<endl; // CDemo d2; // cout<<"d2構造函數對應的值:"<<"&CDemod2="<<(int*)&d2<<", "<<endl; // cout<<"構造CDemo d2; end end end end end endend!!!!@@@@@@!!!"<<endl<<endl<<endl;; // // d2.str=new char[32]; // strcpy(d2.str,"trend micro22222"); // vector<CDemo> *a2=new vector<CDemo>(); // // cout<<"a2->push_back(d2);begin"<<endl; // a2->push_back(d2); // cout<<"a2->push_back(d2);end end end end!!!!@@@@@@!!!"<<endl<<endl<<endl; // // cout<<"析構 a2; begin!!!"<<endl; // cout<<"a2的元素d2對應的值:"<<"&CDemod2="<<(int*) &(a1->at(0)) <<", "<<endl; // deletea2; // cout<<"析構 a2; end end end end@@@@@@"<<endl<<endl<<endl; // // // } cout<<endl<<endl<<endl<< endl; cout<<"下一步就是return0"<<endl; return EXIT_SUCCESS; } 構造CDemo d1; begin!!! 構造函數 d1構造函數對應的值:&CDemo d1=0012FF 構造CDemo d1; end end end end end end end end!!!!!!@@@ 構造vector<CDemo*> *a1; begin!!! 構造vector<CDemo*> *a1; end end end end end end!!!!!!@@@!!! a1->push_back(d1); begin a1->push_back(d1); end end endend!!!!@@@@@@!!! 析構 a1; begin!!! a1的元素d1對應的值:&CDemod1=0012FF 析構 a1; end end end end end end end endend!!!!@@@@@@ 下一步就是return 0 析構函數:&CDemo0=0012FF Press any key to continue 如果放開(kāi) 被隱藏的if() 則結果如下: 構造CDemo d1; begin!!! 構造函數 d1構造函數對應的值:&CDemo d1=0012FF 構造CDemo d1; end end end end end end end end!!!!!!@@@ 構造vector<CDemo*> *a1; begin!!! 構造vector<CDemo*> *a1; end end end end end end!!!!!!@@@!!! a1->push_back(d1); begin a1->push_back(d1); end end end end!!!!@@@@@@!!! 析構 a1; begin!!! a1的元素d1對應的值:&CDemod1=0012FF 析構 a1; end end end end end end end endend!!!!@@@@@@ 構造CDemo d2; begin!!! 構造函數 d2 構造函數對應的值:&CDemo d2=0012FF64, 構造CDemo d2; end end end end end end end!!!!@@@@@@!!! a2->push_back(d2); begin 深拷貝構造函數begin!!! 深拷貝構造函數 endend end end !!!@@@ a2->push_back(d2); end end endend!!!!@@@@@@!!! 析構 a2; begin!!! a2的元素d2對應的值:&CDemod2=20202064, 析構函數:&CDemo0= 析構 a2; end end end end @@@@@@ 析構函數:&CDemo1=0012FF64, str=003809D8 下一步就是return 0 析構函數:&CDemo2=0012FF Press any key to continue
請看下面的程序,說(shuō)說(shuō)會(huì )出現什么問(wèn)題?
#include <iostream>
這個(gè)程序在退出時(shí),會(huì )出問(wèn)題,什么問(wèn)題?重復delete同一片內存,程序崩潰。
我們把析構函數改為如下,可以更清楚的看到這一點(diǎn):
~CDemo()
運行時(shí)我們發(fā)現打印如下信息:
&CDemo0=000309D8, str=
&CDemo1=0013FF70, str=
也就是說(shuō),發(fā)生了CDemo類(lèi)的兩次析構,兩次析構str所指向的同一內存地址空間(兩次str值相同=
為什么?
《程序員面試寶典》第二版,P99,有句解釋“vector對象指針能夠自動(dòng)析構,所以不需要調用deletea1,否則會(huì )造成兩次析構對象”
我切以為這句話(huà)說(shuō)的有點(diǎn)不妥。任何對象如果是通過(guò)new操作符申請了空間,必須顯示的調用delete來(lái)銷(xiāo)毀這個(gè)對象。所以“delete a1; ”這條語(yǔ)句是沒(méi)有錯誤的。
這句話(huà)“vector<CDemo> *a1=new vector<CDemo>(); ”定一個(gè)指針,指向 vector<CDemo>,病用new操作符進(jìn)行了初始化, 我們必須在適當的時(shí)候釋放a1所占的內存空間,所以“delete a1; ”這句話(huà)是沒(méi)有錯誤的。另外,我們必須明白一點(diǎn),釋放vector對象,vector所包含的元素也同時(shí)被釋放。
那到底那里錯誤?
這句a1的聲明和初始化語(yǔ)句
正是這里出了問(wèn)題。a1中的所有CDemo元素的str成員變量沒(méi)有初始化,只有一個(gè)四字節(32位機)指針空間。
“a1->push_back(d1);”這句話(huà)執行完后,a1里的CDemo元素與d1是不同的對象,但是a1里的CDemo元素的str與d1.str指向的是同一塊內存,這從后來(lái)的打印信息就可以看出來(lái)。
我們知道,局部變量,如“CDemo d1; ” 在main函數退出時(shí),自動(dòng)釋放所占內存空間,
那么會(huì )自動(dòng)調用CDemo的析構函數“~CDeme”,問(wèn)題就出在這里。
前面的“delete a1;”已經(jīng)把 d1.str 釋放了(因為a1里的CDemo元素的str與d1.str指向的是同一塊內存),main函數退出時(shí),又要釋放已經(jīng)釋放掉的 d1.str 內存空間,所以程序最后崩潰。
解釋清楚了。
這里最核心的問(wèn)題歸根結底就是淺拷貝和深拷貝的問(wèn)題。如果CDemo類(lèi)添加一個(gè)這樣的拷貝構造函數就可以解決問(wèn)題:
CDemo(const CDemo &cd)
//或者這樣用:
那么在 “delete a1;” a1釋放,同時(shí)a1里面包含的元素(”CDemo*“類(lèi)型,仍然是一個(gè)指針,4字節空間)。
聯(lián)系客服