1. 程式人生 > 程式設計 >C++ 使用new與delete需注意的原則

C++ 使用new與delete需注意的原則

C++的動態記憶體管理是通過new和delete兩個操作來完成的,即用new來申請空間,用delete來釋放空間。在使用new和delete時,注意以下原則。

1.new與delete需一一對應

用new操作申請空間,如果申請成功,必須在以後的某個時刻用delete釋放該空間,既不能忘記釋放,也不能多次釋放。前者會引起記憶體洩露,後者會引起執行時錯誤。如下面的程式。

#include <iostream>
using namespace std;

int main()
{
	int *p;
	p=new int(3);
	if(p)
	{
		delete p;
	}
	delete p;
	return 0;
}

以上程式對指標p所指向的空間進行兩次釋放,這種記憶體錯誤對C++程式危害極大,也是很多人對C++忘而卻步的原因。多次釋放同一塊記憶體空間,並不一定立即引起程式執行錯誤,也不一定會導致程式執行的崩潰,這跟具體的編譯器實現有關。但是,多次釋放同一塊記憶體空間絕對是一個程式設計錯誤,這個程式設計錯誤可能會在其後的某個時刻導致其他的邏輯錯誤的發生,從而給程式的除錯和糾錯帶來困難。考察如下程式。

#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;//假設這句語句是程式設計師不小心加上的
	q=new int(5);
	if(q)
	{
		cout<<q<<endl;
	}
	cout<<(*p)+(*q)<<endl;
	delete p;
	delete q;
}

程式通過編譯,執行結果如下:

003289A0
003289A0
003289A0
10

程式執行過程中會產生中斷。從程式的輸出可以看出,在將指標one所指向的空間釋放後,為指標p申請的空間就是原來one所指向的空間。由於不小心在為p分配空間之後再次使用了delete one,導致q申請到的空間就是原來p所申請的空間,這樣賦給*q的值就改寫了原來p所指向的單元的值,導致最後輸出結果為10。由此可知,多次釋放同一塊記憶體空間,即使不導致程式執行中斷,也會破壞環境,使指標與所對應的空間的隸屬關係出現混亂,從而導致邏輯錯誤。在大型程式設計中,這種邏輯錯誤的查詢會變得十分費時費力。

**注意:**當指標p的值為NULL時,多次使用delete p並不會帶來麻煩,因為釋放空指標的空間實際上不會導致任何操作。所以,將“不用”的指標設定為NULL是一個好的程式設計習慣。

2.new[]與delete[]需一一對應

在申請物件陣列時,需要使用new[]運算子,與之對應,釋放物件陣列時,需要使用delete[]運算子。這一點與C語言有所區別,C中無論申請單個還是多個物件,均使用malloc()/free()函式。首先看一下delete與delete[]運算子的區別。

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執行結果如下:

ctor
ctor
ctor
dtor

segment2執行結果如下:

ctor
ctor
ctor
dtor
dtor
dtor

可以看出,delete與delete[]區別在於釋放物件陣列時,delete只調用了一次解構函式,delete[]呼叫了三次解構函式,完成了物件陣列的釋放。實際上,在使用new和new[]申請記憶體空間時,會申請一段額外的記憶體來儲存使用者申請的記憶體空間大小,元素個數等資訊。當使用delete[]釋放記憶體空間時,會逐個呼叫物件的解構函式並完成最終的記憶體空間的釋放。使用delete釋放物件陣列時,則只會呼叫單個物件的解構函式,造成記憶體洩漏。符號[]告訴編譯器,在delete一塊記憶體時,先去獲取記憶體儲存的元素個數,然後一一清理。所以使用delete釋放new[]申請的記憶體空間和使用delete[]釋放new申請的記憶體空間都錯誤的做法。

具體使用時,需要注意以下兩點:

(1)對於內建資料型別,因為沒有構造和解構函式,所以使用delete和delete[]的效果是一樣的。比如:

int* pDArr=new int[3];
//processing code
delete pDArr;	//等同於delete[] pDArr

對於內建資料型別,雖然可以使用delete完成物件陣列記憶體空間的釋放,但是為了保證程式碼的可讀性,建議使用delete[]來完成。所以,new[]與delete[]使用時應一一對應。

(2)對於經常使用typedef的程式設計師來說,很容易new[]與delete的混用,例如有如下操作:

typedef int Height[NUM];
int* pHeight=new Height;

這個情況應該使用delete還是delete[]呢?答案如下:

delete	pHeight;		//wrong,但容易錯誤地使用delete
delete[] pHeight;	//right

為了避免出現上面的錯誤,建議不要對陣列使用typedef,或者採用STL中的vector代替陣列。

3.建構函式中的new/new[]與解構函式的中delete/delete[]需一一對應

當類的成員中有指標變數時,在建構函式中用new申請空間並且在解構函式中用delete釋放空間是一種“標準的”、安全的做法。例如下面的程式。

#include <iostream>
using namespace std;

class Student
{
	char* name;
public:
	Student()
	{
		cout<<"Default constructor"<<endl;
	}
	Student(char*);
	~Student();
};

Student::Student(char*s)
{
	//Student();//此句執行時報錯,建構函式不能呼叫其他建構函式
	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("張三");
}

程式執行輸出:

In constructor,allocating space
name:張三
In destructor,free space

由於任何一個物件,其建構函式只調用一次,其解構函式也只調用一次,這樣就能保證執行時new和delete操作是一一對應的,也就保證了記憶體管理的安全性。

在C++中,一個建構函式不能呼叫本類的另一個建構函式,其原因就是為了防止建構函式的相互呼叫打破了記憶體申請與釋放之間的這種對應關係。

以上就是C++ 使用new與delete需注意的原則的詳細內容,更多關於C++ new與delete的資料請關注我們其它相關文章!