C++ 使用new與delete需注意的原則
C++的動(dòng)態(tài)內(nèi)存管理是通過(guò)new和delete兩個(gè)操作來(lái)完成的,即用new來(lái)申請(qǐng)空間,用delete來(lái)釋放空間。在使用new和delete時(shí),注意以下原則。
1.new與delete需一一對(duì)應(yīng)
用new操作申請(qǐng)空間,如果申請(qǐng)成功,必須在以后的某個(gè)時(shí)刻用delete釋放該空間,既不能忘記釋放,也不能多次釋放。前者會(huì)引起內(nèi)存泄露,后者會(huì)引起運(yùn)行時(shí)錯(cuò)誤。如下面的程序。
#include <iostream> using namespace std; int main() { int *p; p=new int(3); if(p) { delete p; } delete p; return 0; }
以上程序?qū)χ羔榩所指向的空間進(jìn)行兩次釋放,這種內(nèi)存錯(cuò)誤對(duì)C++程序危害極大,也是很多人對(duì)C++忘而卻步的原因。多次釋放同一塊內(nèi)存空間,并不一定立即引起程序運(yùn)行錯(cuò)誤,也不一定會(huì)導(dǎo)致程序運(yùn)行的崩潰,這跟具體的編譯器實(shí)現(xiàn)有關(guān)。但是,多次釋放同一塊內(nèi)存空間絕對(duì)是一個(gè)編程錯(cuò)誤,這個(gè)編程錯(cuò)誤可能會(huì)在其后的某個(gè)時(shí)刻導(dǎo)致其他的邏輯錯(cuò)誤的發(fā)生,從而給程序的調(diào)試和糾錯(cuò)帶來(lái)困難。考察如下程序。
#include <iostream> using namespace std; int main() { int *p,*q,*one; one=new int; if(one) { cout<<one<<endl; } delete one; p=new int(3); if(p) { cout<<p<<endl; } delete one;//假設(shè)這句語(yǔ)句是程序員不小心加上的 q=new int(5); if(q) { cout<<q<<endl; } cout<<(*p)+(*q)<<endl; delete p; delete q; }
程序通過(guò)編譯,運(yùn)行結(jié)果如下:
003289A0
003289A0
003289A0
10
程序運(yùn)行過(guò)程中會(huì)產(chǎn)生中斷。從程序的輸出可以看出,在將指針one所指向的空間釋放后,為指針p申請(qǐng)的空間就是原來(lái)one所指向的空間。由于不小心在為p分配空間之后再次使用了delete one,導(dǎo)致q申請(qǐng)到的空間就是原來(lái)p所申請(qǐng)的空間,這樣賦給*q的值就改寫(xiě)了原來(lái)p所指向的單元的值,導(dǎo)致最后輸出結(jié)果為10。由此可知,多次釋放同一塊內(nèi)存空間,即使不導(dǎo)致程序運(yùn)行中斷,也會(huì)破壞環(huán)境,使指針與所對(duì)應(yīng)的空間的隸屬關(guān)系出現(xiàn)混亂,從而導(dǎo)致邏輯錯(cuò)誤。在大型程序設(shè)計(jì)中,這種邏輯錯(cuò)誤的查找會(huì)變得十分費(fèi)時(shí)費(fèi)力。
**注意:**當(dāng)指針p的值為NULL時(shí),多次使用delete p并不會(huì)帶來(lái)麻煩,因?yàn)獒尫趴罩羔樀目臻g實(shí)際上不會(huì)導(dǎo)致任何操作。所以,將“不用”的指針設(shè)置為NULL是一個(gè)好的編程習(xí)慣。
2.new[]與delete[]需一一對(duì)應(yīng)
在申請(qǐng)對(duì)象數(shù)組時(shí),需要使用new[]運(yùn)算符,與之對(duì)應(yīng),釋放對(duì)象數(shù)組時(shí),需要使用delete[]運(yùn)算符。這一點(diǎn)與C語(yǔ)言有所區(qū)別,C中無(wú)論申請(qǐng)單個(gè)還是多個(gè)對(duì)象,均使用malloc()/free()函數(shù)。首先看一下delete與delete[]運(yùn)算符的區(qū)別。
class Test { public: Test() { cout<<"ctor"<<endl; } ~Test() { cout << "dtor" << endl; } }; //segment1 Test* pArray1 = new Test[3]; delete pArray1; //segment2 Test* pArray2 = new Test[3]; delete[] pArray2;
其中代碼片段segment1運(yùn)行結(jié)果如下:
ctor
ctor
ctor
dtor
segment2運(yùn)行結(jié)果如下:
ctor
ctor
ctor
dtor
dtor
dtor
可以看出,delete與delete[]區(qū)別在于釋放對(duì)象數(shù)組時(shí),delete只調(diào)用了一次析構(gòu)函數(shù),delete[]調(diào)用了三次析構(gòu)函數(shù),完成了對(duì)象數(shù)組的釋放。實(shí)際上,在使用new和new[]申請(qǐng)內(nèi)存空間時(shí),會(huì)申請(qǐng)一段額外的內(nèi)存來(lái)保存用戶申請(qǐng)的內(nèi)存空間大小,元素個(gè)數(shù)等信息。當(dāng)使用delete[]釋放內(nèi)存空間時(shí),會(huì)逐個(gè)調(diào)用對(duì)象的析構(gòu)函數(shù)并完成最終的內(nèi)存空間的釋放。使用delete釋放對(duì)象數(shù)組時(shí),則只會(huì)調(diào)用單個(gè)對(duì)象的析構(gòu)函數(shù),造成內(nèi)存泄漏。符號(hào)[]告訴編譯器,在delete一塊內(nèi)存時(shí),先去獲取內(nèi)存保存的元素個(gè)數(shù),然后一一清理。所以使用delete釋放new[]申請(qǐng)的內(nèi)存空間和使用delete[]釋放new申請(qǐng)的內(nèi)存空間都錯(cuò)誤的做法。
具體使用時(shí),需要注意以下兩點(diǎn):
(1)對(duì)于內(nèi)置數(shù)據(jù)類(lèi)型,因?yàn)闆](méi)有構(gòu)造和析構(gòu)函數(shù),所以使用delete和delete[]的效果是一樣的。比如:
int* pDArr=new int[3]; //processing code delete pDArr; //等同于delete[] pDArr
對(duì)于內(nèi)置數(shù)據(jù)類(lèi)型,雖然可以使用delete完成對(duì)象數(shù)組內(nèi)存空間的釋放,但是為了保證代碼的可讀性,建議使用delete[]來(lái)完成。所以,new[]與delete[]使用時(shí)應(yīng)一一對(duì)應(yīng)。
(2)對(duì)于經(jīng)常使用typedef的程序員來(lái)說(shuō),很容易new[]與delete的混用,例如有如下操作:
typedef int Height[NUM]; int* pHeight=new Height;
這個(gè)情況應(yīng)該使用delete還是delete[]呢?答案如下:
delete pHeight; //wrong,但容易錯(cuò)誤地使用delete delete[] pHeight; //right
為了避免出現(xiàn)上面的錯(cuò)誤,建議不要對(duì)數(shù)組使用typedef,或者采用STL中的vector代替數(shù)組。
3.構(gòu)造函數(shù)中的new/new[]與析構(gòu)函數(shù)的中delete/delete[]需一一對(duì)應(yīng)
當(dāng)類(lèi)的成員中有指針變量時(shí),在構(gòu)造函數(shù)中用new申請(qǐng)空間并且在析構(gòu)函數(shù)中用delete釋放空間是一種“標(biāo)準(zhǔn)的”、安全的做法。例如下面的程序。
#include <iostream> using namespace std; class Student { char* name; public: Student() { cout<<"Default constructor"<<endl; } Student(char*); ~Student(); }; Student::Student(char*s) { //Student();//此句運(yùn)行時(shí)報(bào)錯(cuò),構(gòu)造函數(shù)不能調(diào)用其他構(gòu)造函數(shù) cout<<"In constructor,allocating space"<<endl; name=new char[strlen(s)+1]; strcpy(name,s); cout<<"name:"<<name<<endl; } Student::~Student() { cout<<"In destructor, free space"<<endl; delete name; } int main() { Student s1("張三"); }
程序運(yùn)行輸出:
In constructor,allocating space
name:張三
In destructor, free space
由于任何一個(gè)對(duì)象,其構(gòu)造函數(shù)只調(diào)用一次,其析構(gòu)函數(shù)也只調(diào)用一次,這樣就能保證運(yùn)行時(shí)new和delete操作是一一對(duì)應(yīng)的,也就保證了內(nèi)存管理的安全性。
在C++中,一個(gè)構(gòu)造函數(shù)不能調(diào)用本類(lèi)的另一個(gè)構(gòu)造函數(shù),其原因就是為了防止構(gòu)造函數(shù)的相互調(diào)用打破了內(nèi)存申請(qǐng)與釋放之間的這種對(duì)應(yīng)關(guān)系。
以上就是C++ 使用new與delete需注意的原則的詳細(xì)內(nèi)容,更多關(guān)于C++ new與delete的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
DSP中浮點(diǎn)轉(zhuǎn)定點(diǎn)運(yùn)算--定點(diǎn)數(shù)模擬浮點(diǎn)數(shù)運(yùn)算及常見(jiàn)的策略
本文主要講解DSP中定點(diǎn)數(shù)模擬浮點(diǎn)數(shù)運(yùn)算及常見(jiàn)的策略,具有參考價(jià)值,需要的朋友可以參考一下。2016-06-06C++實(shí)現(xiàn)打地鼠游戲設(shè)計(jì)
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)打地鼠游戲設(shè)計(jì),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-12-12使用root權(quán)限運(yùn)行自己所編譯程序的解決方法
本篇文章介紹了,使用root權(quán)限運(yùn)行自己所編譯程序的解決方法。需要的朋友參考下2013-05-05C/C++產(chǎn)生隨機(jī)數(shù)函數(shù)簡(jiǎn)單介紹
這篇文章主要為大家詳細(xì)介紹了C/C++產(chǎn)生隨機(jī)數(shù)函數(shù)的實(shí)現(xiàn)方法,如何使用C/C++產(chǎn)生隨機(jī)數(shù)函數(shù),感興趣的小伙伴們可以參考一下2016-04-04C++使用宏函數(shù)實(shí)現(xiàn)單例模板詳解
在我們?nèi)粘i_(kāi)發(fā)中,無(wú)可避免需要使用單例模式進(jìn)行設(shè)計(jì)類(lèi)對(duì)象。這篇文章主要介紹了如何使用宏函數(shù)實(shí)現(xiàn)單例模板,感興趣的小伙伴可以了解一下2023-02-02