1?
引言
在程序執(zhí)行時,訪問內(nèi)存是必需的,總的來說在
C ++
語言中,內(nèi)存分配有下述二種力一式:
在靜態(tài)存儲區(qū)中分配,也稱靜態(tài)內(nèi)存分配在這種分配方式中,內(nèi)存在程序編譯時就己經(jīng)分配好,這塊內(nèi)存在程序的整個運(yùn)行期問都存在。全局變量、
static
變量占用的內(nèi)存在靜態(tài)存儲區(qū)中,這此變量我們稱之為靜態(tài)對象。
在棧中分配,也稱局部內(nèi)存分配。函數(shù)內(nèi)的局部變量、函數(shù)的形式參數(shù)、函數(shù)執(zhí)行結(jié)束后返回的地址等都是在棧中分配內(nèi)存,函數(shù)執(zhí)行結(jié)束,這此內(nèi)存單元通過執(zhí)行出棧指令釋放。棧內(nèi)存分配和釋放指令內(nèi)置于處理器的指令集中,效率很高。在棧中分配的對象,我們稱之為局部對象。不過,局部對象的內(nèi)存分配和回收不用程序管,在程序執(zhí)行時由執(zhí)行系統(tǒng)動態(tài)進(jìn)行。
在堆中分配,也稱動態(tài)內(nèi)存分配程序在運(yùn)行的時候用操作符
new
申請長度任意的內(nèi)存,不再需要時用操作符
delete
釋放內(nèi)存。在堆中分配的對象,我們稱為動態(tài)對象。
在棧上創(chuàng)建對象既方便又理想,但更一般的情況是,在程序執(zhí)行的任何時候,需要根據(jù)來自程序外部的信息創(chuàng)建對象和銷毀對象,這就需要對內(nèi)存進(jìn)行動態(tài)分配和回收,而且這部分工作必須由程序來管,所以也是
C++
內(nèi)存分配和回收的重點(diǎn)和難點(diǎn)。下面討論在
C ++
程序設(shè)計(jì)中進(jìn)行內(nèi)存動態(tài)分配和回收的方法及存在的問題和解決方法。
2??
數(shù)組對象的動態(tài)分配和回收
在
C ++
程序中,數(shù)組的內(nèi)存分配有兩種,靜態(tài)分配和動態(tài)分配。在編譯時進(jìn)行的分配我們稱為靜態(tài)分配,在執(zhí)行時進(jìn)行的分配我們稱為動態(tài)分配。對于一般的程序設(shè)計(jì)者,習(xí)慣更多地使用靜態(tài)數(shù)組。然血靜態(tài)數(shù)組的長度在編譯時就確定了,有時不能滿足應(yīng)用的需要,更一般的情況是,數(shù)組的長度在程序執(zhí)行時根據(jù)輸入的數(shù)據(jù)才能確定,如求
n
個整數(shù)的最大數(shù),
n
的值從鍵盤接收,此時,存放
n
個整數(shù)的數(shù)組必須在執(zhí)行時用
new
操作符動態(tài)創(chuàng)建。完整的例程如下:
#include <iostream.h>
voidmain()
{cout<<"Please input a integer to n:";
cin>>n;
int *p=new int[n];
//
為長度為
n
的值確定的動態(tài)一維數(shù)組分配內(nèi)存
max=a[0];
for (int i=1;i<n;i++)
if (a[i]>a[0])a[0]=a[i];
cout<<"The max value is:"+a[0];
delete[] p;//
動態(tài)刪除數(shù)組
i.
用的主存空間
}
如果我們使用靜態(tài)數(shù)組來存放
n
個數(shù)據(jù),假定在源程序中符號常量
n
初始化為
100
,那么程序只能處理
100
個數(shù)據(jù)以內(nèi)的整數(shù),可見程序的使用范圍小,也可以說程序執(zhí)行時的靈活性小。從上述例子中我們可以看出使用動態(tài)數(shù)組的好處,即程序能處理任意個同類型的數(shù)據(jù),只要主存空間不受限制。
在使用動態(tài)數(shù)組時必須特別注意,當(dāng)數(shù)組不再使用,應(yīng)該用
delete[]
操作符及時刪除,使得其占用過的內(nèi)存能再次利用,否則,隨著系統(tǒng)不斷動態(tài)分配內(nèi)存,可用的主存空間會越來越小,甚至?xí)谋M。
相對一維數(shù)組的動態(tài)分配形式,多維數(shù)組的動態(tài)分配形式相對復(fù)雜,且初學(xué)者使用時非常容易出錯。下面描述了二個多維數(shù)組的動態(tài)分配形式。
char **p1=new char[3][n];
double ***p2=new double[4][m][n];
//
動態(tài)創(chuàng)建
2
維、
3
維數(shù)組
,m,n
可以是常量或變量
在創(chuàng)建多維數(shù)組時,必須十分注意指針
p1
和
p2
的類型,稍不注意,會將
p1
和
p2
聲明為
char *pl
和
double *p2
,導(dǎo)致編譯時產(chǎn)生和類型有關(guān)的錯誤。另外,創(chuàng)建多維數(shù)組的第一維的大小必須由常量指定。
關(guān)于多維數(shù)組的回收和一維數(shù)組形式一致,下面的兩種形式表示刪除上述創(chuàng)建的二維和二維數(shù)組。
(1 ) delete p1 [];
(2) delete p2[];
3???
一般對象的動態(tài)分配和回收
非數(shù)組對象
(
一般對象
)
的動態(tài)分配和回收相對于數(shù)組對象的創(chuàng)建和回收,要簡單此。
?????? 3.1???
一般對象的動態(tài)分配和回收
C ++
語言中用
new
操作符為一般對象動態(tài)分配內(nèi)存。為能夠在程序中存取
new
所創(chuàng)建的對象,必須使一個指針指向所創(chuàng)建的對象。例如,下述程序段創(chuàng)建一個初值為
(5,5)
的的屏幕上的點(diǎn)類
point
的對象,指向該對象的指針仇被賦給了指針對象
ptr
:
class point//
屏一
SIT
上的點(diǎn)類
{
x,y;
public:
point(int x1=0, int y1=0){x=x1 ;y=y1;}
int getx(){returnx;}
int gety(){returny;}
};
voidmain()
{point *ptr=newpoint(5,5);
//
創(chuàng)建
point
類型的對象,初始值為
(5,5)
Cout<<"x="<<pt->getx()+'\t'<<'y='<<pt->gety()+endl;
……
delete p;
常量對象的生存期也用
delete
來結(jié)束,如上面的一行語句。與創(chuàng)建的非常量對象不完全相同,創(chuàng)建的
const
對象有一些特殊的屬性。首先,
const
對象必須被初始化,如果省略了括號中的初始值,就會產(chǎn)生編譯錯誤。第二,用
new
表達(dá)式返回的值作為初始值的指針必須是一個指向
const
類型的指針。此外,我們不能創(chuàng)建元素類型為基木數(shù)據(jù)類型的
const
數(shù)組,因?yàn)?/span>
const
數(shù)組不能被初始化,如象下而那樣聲明
const
數(shù)組會導(dǎo)致編譯錯誤:
const int *pt=new const int[10];
??????? 3.3??
定位
new
表達(dá)式
new
表達(dá)式還允許將對象創(chuàng)建在己被分配好的內(nèi)存中。這種形式的
new
表達(dá)式被稱為定位
new
表達(dá)式,其一般形式為:
new (place_ address) type
其中
place_ address
是個指針表達(dá)式,
I(IJ type
是個類型表達(dá)式。必須注意,為了使用這種形式的二
w
表達(dá)式,我們必須包含頭文件
<new>
。這個功能允許程序員預(yù)分配大量的內(nèi)存供以后通過這種形式的
new
表達(dá)創(chuàng)建對象。請看下面的例子:
#include <iostream>
#include <new>
using namespace std;
const int num = 10;
class block
{
int val;
public:
int block(int a=0){val=a;}
int getval(){ return val;}
};
char 'buf=new char[sizeof(block)'num];
//
預(yù)分配內(nèi)存,但沒有
block
對象
voidmain()
{block 'p=new (buf) block;
if (p->getval() == 0)//
檢查一個
block
對象是否被放在
buf
中
cout<<"new expression worked!"+endl;
delete[] buf;//
刪除
buf
指
I}}J
的字符緩沖,之后不能再訪
不存在與定位
new
表達(dá)式相匹配的
delete
表達(dá)式。其實(shí)我們并不需要這樣的
delete
表達(dá)式,因?yàn)槎ㄎ?/span>
new
表達(dá)式并不分配內(nèi)存。在上面的例了中,我們刪除的不是
p
指向的內(nèi)存,而是
buf
指向的內(nèi)存。當(dāng)程序不再需要字符緩沖時,
buf
指向的內(nèi)存被刪除,此時字符緩沖中的任何對象的生命期都結(jié)束了,在上面的例了中,
p
就不再指向一個有效的
block
類的對象了。
4
使用動態(tài)內(nèi)存常見的問題與對策
和
C
語言提供的動態(tài)內(nèi)存分配和回收方法相比,
new
和
delete
比
C
語言的
malloc()
和
free()
函數(shù)使用起來更加方便和安全,但同時也增加了各種錯誤發(fā)生的機(jī)會,我們總結(jié)了如下幾個方面的錯誤。
?????? 4.l?
未使用相同形式的
new
和
delete
首先我們來看下面的語句序列
:
int *pt=new int[30];
……
delete pt;
這兩條語句看似沒有問題,而且編譯器也不會給出錯誤或警告信息,其實(shí)己經(jīng)發(fā)生了內(nèi)存泄漏。因?yàn)?/span>
30
個整型對象有
29
個都未被釋放內(nèi)存空問,上述代碼表示釋放了數(shù)組的第一個元素對象。為什么會這樣呢,因?yàn)樵谑褂?/span>
delete
表達(dá)式時,編譯器不知道即將被刪除的指針指向的是單一對象或數(shù)組對象,它只會根據(jù)
delete
的使用形式來判斷,帶了下標(biāo)符“
[]
”的則認(rèn)為是刪除數(shù)組對象,否則就認(rèn)為是刪除單一對象。因此,解決該問題的方法很簡單,當(dāng)使用
new
時帶了下標(biāo)符“
[]
”,使用
delete
時也應(yīng)帶
[]
;當(dāng)你使用
new
時未帶“
[]
”,使用
delete
時也不應(yīng)帶“
[]
”。
??????? 4.2?
內(nèi)存不足的問題
如果
new
操作找不到足夠大的內(nèi)存塊,則引發(fā)
bad_alloc
標(biāo)準(zhǔn)類型的異常,為了提高程序的可靠性,程序中必須能捕獲這種內(nèi)存分配失敗引發(fā)的異常,這是一般程序員容易忽視的問題。不過因?yàn)楫惓L幚韴?zhí)行效率不高,所以可用下述方法使
new
操作返回一個
NULL
而不拋出一個異常。例如程序段:
B*p=new (nothrow) B;/*
對象
nothrow
是在
new
中定義的常量,
指出如果
new
操作失敗則返回
NULL
而不產(chǎn)生異常
*/
if (!p){cout<<"allocation failure"<<endl;return;}
4.3?
內(nèi)存被釋放后使用指針的問題
指向并不存在的內(nèi)存的指針,我們稱之為“野指針”。例如程序段
:
char *p=new char[6];
strcpy(p,"hello");
delete[] p;//
此時
p
成了野指針
……
if (p) strcpy(p,"world");//
執(zhí)行時出錯
……
為了能有效解決上述問題,一個簡單的辦法就是在內(nèi)存釋放后,將指針賦值為
VULI
。如
:
delete[] p;
p=N ULL;
4.4
沒有和構(gòu)造函數(shù)配對的析構(gòu)函數(shù)
如果在一個類對象在創(chuàng)建時要動態(tài)分配內(nèi)存,就會在構(gòu)造
函數(shù)中用
new
分配。例如
:
class array
{int *p;
public:
array(int i){p=new int[i];}...
};
很明顯,在構(gòu)造函數(shù)中為
array
類對象分配的內(nèi)存一直不能被釋放,隨著對象的多次產(chǎn)生,就會產(chǎn)生嚴(yán)重的內(nèi)存泄漏問題。解決辦法是為這種類增加這樣一個析構(gòu)函數(shù)
:array ::array{delete [] p;}
。
5 ?
結(jié)束語
在
C ++
程序設(shè)計(jì)中,會經(jīng)常進(jìn)行內(nèi)存的動態(tài)分配和回收。木文對
C++
的動態(tài)內(nèi)存技術(shù)進(jìn)行了討論,并對常見問題提出了可行的解決方法。其實(shí),用好
new
和
delete
是一門技術(shù),它能減少程序中與使用指針有關(guān)的運(yùn)行錯誤,從而減輕調(diào)試程序的難度,另外它還能提高程序的可靠性(健壯性)。