博客網(wǎng)站如何建設(shè)互聯(lián)網(wǎng)推廣渠道
在C++中動態(tài)內(nèi)存的分配技術(shù)可以保證程序在允許過程中按照實際需要申請適量的內(nèi)存,使用結(jié)束后還可以釋放,這種在程序運行過程中申請和釋放的存儲單元也稱為堆。 申請和釋放過程一般稱為建立和刪除。
在C++程序中,建立和刪除堆對象使用兩個運算符:new和delete。
1.運算符new的功能是動態(tài)分配內(nèi)存,或者稱為動態(tài)創(chuàng)建堆對象,語法形式為:
new 數(shù)據(jù)類型(初始化列表參數(shù));
該語句在程序運行過程中申請分配用于存放指定類型數(shù)據(jù)的內(nèi)存空間,并根據(jù)初始化參數(shù)列表中給出的值進行初始化。如果內(nèi)存申請成功,new運算符便返回一個指向新分配內(nèi)存首地址的類型的指針,可以通過這個指針對該內(nèi)存空間進行訪;如果申請失敗,會拋出異常。
(1)對于基本類型的對象
如果建立的對象是一個基本類型的變量,初始化過程就是賦值,例如:
int *p;
p=new int(2);
動態(tài)分配了用于存放int型數(shù)據(jù)的內(nèi)存空間,并將初值2存入該空間中,然后將首地址賦給指針p。
【注意】對于基本數(shù)據(jù)類型,如果不希望在內(nèi)存分配后設(shè)定初值,可以把括號省去,例如:
int *p=new int;
如果保留括號,但括號中不寫任何數(shù)值,則表示用0對該對象初始化,例如:
int *p=new int();
(2)對于類類型的對象
要根據(jù)初始化參數(shù)列表的參數(shù)類型和個數(shù)調(diào)用該類的構(gòu)造函數(shù)。
在用new建立一個對象時,如果該類存在用戶自定義的默認構(gòu)造函數(shù),則“new T”和“new T()”這兩種寫法的效果時相同的,都會調(diào)用這個默認的構(gòu)造函數(shù)。但若用戶沒有定義默認的構(gòu)造函數(shù),使用“new T”創(chuàng)建對象時,會調(diào)用系統(tǒng)生成的隱含的默認構(gòu)造函數(shù);使用“new T()”創(chuàng)建對象時,系統(tǒng)除了執(zhí)行默認構(gòu)造函數(shù)會執(zhí)行的操作外,還會為基本數(shù)據(jù)類型和指針類型的成員用0賦初值,而且這一過程是遞歸的。也就是說,如果該對象的某個成員對象也沒有用戶自定義的默認構(gòu)造函數(shù),那么對該成員的基本數(shù)據(jù)類型和指針類型的成員,同樣會被以0賦初值。
2.運算符delete是用來刪除由new建立的對象,釋放指針?biāo)赶虻膬?nèi)存空間。
格式為:
delete 指針名;
如果刪除的是對象,該對象的析構(gòu)函數(shù)將被調(diào)用。對于new建立的對象,只能用delete進行一次刪除操作,如果同一內(nèi)存空間多次使用delete進行刪除將會導(dǎo)致運行錯誤。
【注意】用new分配的內(nèi)存,必須用delete加以釋放,否則會導(dǎo)致動態(tài)內(nèi)存分配的內(nèi)存無法回收,使得程序占據(jù)的內(nèi)存越來越大,這叫做“內(nèi)存泄漏”。
【例】動態(tài)創(chuàng)建對象
class Point
{
public:Point() :x(0), y(0){cout << "調(diào)用默認構(gòu)造函數(shù)" << endl;}Point(int x, int y) :x(x), y(y){cout << "調(diào)用構(gòu)造函數(shù)" << endl;}~Point(){cout << "調(diào)用析構(gòu)函數(shù)" << endl;}int getX() { return x; }int getY() { return y; }void move(int newX, int newY){x = newX;y = newY;}
private:int x, y;
};int main()
{cout << "創(chuàng)建第一個對象:" << endl;Point* p1 = new Point;//動態(tài)創(chuàng)建對象,沒有給出參數(shù)列表,因此調(diào)用默認的構(gòu)造函數(shù)delete p1;//刪除對象,自動調(diào)用析構(gòu)函數(shù)cout << "創(chuàng)建第一個對象:" << endl;Point* p2 = new Point(1,2);//動態(tài)創(chuàng)建對象,并給出參數(shù)列表,因此調(diào)用有參數(shù)的構(gòu)造函數(shù)delete p2;//刪除對象,自動調(diào)用析構(gòu)函數(shù)return 0;
}
運行結(jié)果:
3.使用運算符new也可以創(chuàng)建數(shù)組對象,這時需要給出數(shù)組的結(jié)構(gòu)說明。用new運算符動態(tài)創(chuàng)建一維數(shù)組的語法形式為:
new 類型名[數(shù)組長度];
其中數(shù)組長度指出了數(shù)組元素的個數(shù),它可以是任何能夠得到正整數(shù)值得表達式。
用new動態(tài)創(chuàng)建一維數(shù)組時,在方括號后面仍然可以加小括號“()”,但小括號內(nèi)不能帶參數(shù)。是否加“()”得區(qū)別在于,不加“()”,則對數(shù)組每個元素得初始化,與執(zhí)行“new T”時所進行得初始化方式相同;加“()”,則與執(zhí)行“new T”所進行初始化得方式相同。例如,如果這樣動態(tài)生成一個整型數(shù)組:
int *p=new int[10]();
則可以方便地為動態(tài)創(chuàng)建的數(shù)組用()初始化。
如果是用new建立得數(shù)組,用delete刪除時在指針名前面要加“[]”,格式如下:
delete []指針名;
【例】動態(tài)創(chuàng)建對象數(shù)組
class Point
{
public:Point() :x(0), y(0){cout << "調(diào)用默認構(gòu)造函數(shù)" << endl;}Point(int x, int y) :x(x), y(y){cout << "調(diào)用構(gòu)造函數(shù)" << endl;}~Point(){cout << "調(diào)用析構(gòu)函數(shù)" << endl;}int getX() { return x; }int getY() { return y; }void move(int newX, int newY){x = newX;y = newY;cout <<"(" <<x <<"," << y<<")" << endl;}
private:int x, y;
};int main()
{Point* p = new Point[2];p[0].move(5, 10);p[1].move(15, 20);cout << "刪除對象:" << endl;delete[]p;return 0;
}
運行結(jié)果:
這是利用動態(tài)內(nèi)存分配操作實現(xiàn)了數(shù)組得動態(tài)創(chuàng)建,使得數(shù)組元素得個數(shù)可以根據(jù)運行時得需要而確定。但是建立和刪除數(shù)組得過程使得程序略顯繁瑣,更好得方法是將數(shù)組得建立和刪除封裝起來,形成一個動態(tài)數(shù)組類。
另外,在動態(tài)數(shù)組類中,通過類得成員函數(shù)訪問數(shù)組元素,可以每次在訪問之間檢查一下下標(biāo)是否越界,使得數(shù)組下標(biāo)越界得錯誤能夠及早發(fā)現(xiàn)。這種檢查,可以通過C++的assert來進行。assert的含義是“斷言”,它是標(biāo)準(zhǔn)C++的cassert頭文件中定義的一個宏,用來判斷一個條件表達式的值是否為true,如果不為true,則程序會中止,并且報錯,這樣就很容易將錯誤定位。一個程序一般可以以兩種模式編譯——調(diào)試(debug)模式和發(fā)行(release)模式,assert只在調(diào)試模式下生效,而在發(fā)行模式下不執(zhí)行任何操作,這樣兼顧了調(diào)試模式的調(diào)試需求和發(fā)行模式的效率需求。
【注意】由于assert只在調(diào)試模式下生效,一般用assert只是檢查程序本身的邏輯錯誤,而用戶的不當(dāng)輸入造成的錯誤,則應(yīng)當(dāng)用其他方式加以處理。
【例】動態(tài)數(shù)組類
class Point
{
public:Point() :x(0), y(0){cout << "調(diào)用默認構(gòu)造函數(shù)" << endl;}Point(int x, int y) :x(x), y(y){cout << "調(diào)用構(gòu)造函數(shù)" << endl;}~Point(){cout << "調(diào)用析構(gòu)函數(shù)" << endl;}int getX() { return x; }int getY() { return y; }void move(int newX, int newY){x = newX;y = newY;cout << "(" << x << "," << y << ")" << endl;}
private:int x, y;
};
//動態(tài)數(shù)組類
class Arr
{
public:Arr(int size) :size(size){points = new Point[size];}~Arr(){cout << "刪除對象:" << endl;delete[]points;}//獲得下標(biāo)為index的數(shù)組元素Point& element(int index){assert(index >= 0 && index < size);//如果數(shù)組下標(biāo)越界,程序中止return points[index];}
private:Point* points;//指向動態(tài)數(shù)組的首地址int size;//數(shù)組大小
};int main()
{int count;cout << "請輸入要創(chuàng)建的對象的個數(shù):";cin >> count;Arr points(count);//創(chuàng)建對象數(shù)組points.element(0).move(5,0);//訪問數(shù)組元素的成員points.element(1).move(15,20);return 0;
}
運行結(jié)果:
在main函數(shù)中,只是建立一個Arr類的對象,對象的初始化參數(shù)size指定了數(shù)組元素的個數(shù),創(chuàng)建和刪除對象數(shù)組的過程都由Arr類的構(gòu)造函數(shù)和析構(gòu)函數(shù)完成。