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