C++內(nèi)存分配與管理_第1頁
C++內(nèi)存分配與管理_第2頁
C++內(nèi)存分配與管理_第3頁
C++內(nèi)存分配與管理_第4頁
C++內(nèi)存分配與管理_第5頁
已閱讀5頁,還剩21頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

./在C++中,內(nèi)存分成5個區(qū),他們分別是堆、棧、自由存儲區(qū)、全局/靜態(tài)存儲區(qū)和常量存儲區(qū)。棧,就是那些由編譯器在需要的時候分配,在不需要的時候自動清楚的變量的存儲區(qū)。里面的變量通常是局部變量、函數(shù)參數(shù)等。堆,就是那些由new分配的內(nèi)存塊,他們的釋放編譯器不去管,由我們的應(yīng)用程序去控制,一般一個new就要對應(yīng)一個delete。如果程序員沒有釋放掉,那么在程序結(jié)束后,操作系統(tǒng)會自動回收。自由存儲區(qū),就是那些由malloc等分配的內(nèi)存塊,他和堆是十分相似的,不過它是用free來結(jié)束自己的生命的。全局/靜態(tài)存儲區(qū),全局變量和靜態(tài)變量被分配到同一塊內(nèi)存中,在以前的C語言中,全局變量又分為初始化的和未初始化的,在C++里面沒有這個區(qū)分了,他們共同占用同一塊內(nèi)存區(qū)。常量存儲區(qū),這是一塊比較特殊的存儲區(qū),他們里面存放的是常量,不允許修改〔當(dāng)然,你要通過非正當(dāng)手段也可以修改,而且方法很多,在《const的思考》一文中,我給出了6種方法〕明確區(qū)分堆與棧在bbs上,堆與棧的區(qū)分問題,似乎是一個永恒的話題,由此可見,初學(xué)者對此往往是混淆不清的,所以我決定拿他第一個開刀。首先,我們舉一個例子:voidf<>{int*p=newint[5];}這條短短的一句話就包含了堆與棧,看到new,我們首先就應(yīng)該想到,我們分配了一塊堆內(nèi)存,那么指針p呢?他分配的是一塊棧內(nèi)存,所以這句話的意思就是:在棧內(nèi)存中存放了一個指向一塊堆內(nèi)存的指針p。在程序會先確定在堆中分配內(nèi)存的大小,然后調(diào)用operatornew分配內(nèi)存,然后返回這塊內(nèi)存的首地址,放入棧中,他在VC6下的匯編代碼如下:00401028push14h0040102Acalloperatornew<00401060>0040102Faddesp,400401032movdwordptr[ebp-8],eax00401035moveax,dwordptr[ebp-8]00401038movdwordptr[ebp-4],eax這里,我們?yōu)榱撕唵尾]有釋放內(nèi)存,那么該怎么去釋放呢?是deletep么?澳,錯了,應(yīng)該是delete[]p,這是為了告訴編譯器:我刪除的是一個數(shù)組,VC6就會根據(jù)相應(yīng)的Cookie信息去進(jìn)行釋放內(nèi)存的工作。好了,我們回到我們的主題:堆和棧究竟有什么區(qū)別?主要的區(qū)別由以下幾點:1、管理方式不同;2、空間大小不同;3、能否產(chǎn)生碎片不同;4、生長方向不同;5、分配方式不同;6、分配效率不同;管理方式:對于棧來講,是由編譯器自動管理,無需我們手工控制;對于堆來說,釋放工作由程序員控制,容易產(chǎn)生memoryleak??臻g大?。阂话銇碇v在32位系統(tǒng)下,堆內(nèi)存可以達(dá)到4G的空間,從這個角度來看堆內(nèi)存幾乎是沒有什么限制的。但是對于棧來講,一般都是有一定的空間大小的,例如,在VC6下面,默認(rèn)的??臻g大小是1M〔好像是,記不清楚了〕。當(dāng)然,我們可以修改:打開工程,依次操作菜單如下:Project->Setting->Link,在Category中選中Output,然后在Reserve中設(shè)定堆棧的最大值和commit。注意:reserve最小值為4Byte;commit是保留在虛擬內(nèi)存的頁文件里面,它設(shè)置的較大會使棧開辟較大的值,可能增加內(nèi)存的開銷和啟動時間。碎片問題:對于堆來講,頻繁的new/delete勢必會造成內(nèi)存空間的不連續(xù),從而造成大量的碎片,使程序效率降低。對于棧來講,則不會存在這個問題,因為棧是先進(jìn)后出的隊列,他們是如此的一一對應(yīng),以至于永遠(yuǎn)都不可能有一個內(nèi)存塊從棧中間彈出,在他彈出之前,在他上面的后進(jìn)的棧內(nèi)容已經(jīng)被彈出,詳細(xì)的可以參考數(shù)據(jù)結(jié)構(gòu),這里我們就不再一一討論了。生長方向:對于堆來講,生長方向是向上的,也就是向著內(nèi)存地址增加的方向;對于棧來講,它的生長方向是向下的,是向著內(nèi)存地址減小的方向增長。分配方式:堆都是動態(tài)分配的,沒有靜態(tài)分配的堆。棧有2種分配方式:靜態(tài)分配和動態(tài)分配。靜態(tài)分配是編譯器完成的,比如局部變量的分配。動態(tài)分配由alloca函數(shù)進(jìn)行分配,但是棧的動態(tài)分配和堆是不同的,他的動態(tài)分配是由編譯器進(jìn)行釋放,無需我們手工實現(xiàn)。分配效率:棧是機(jī)器系統(tǒng)提供的數(shù)據(jù)結(jié)構(gòu),計算機(jī)會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執(zhí)行,這就決定了棧的效率比較高。堆則是C/C++函數(shù)庫提供的,它的機(jī)制是很復(fù)雜的,例如為了分配一塊內(nèi)存,庫函數(shù)會按照一定的算法〔具體的算法可以參考數(shù)據(jù)結(jié)構(gòu)/操作系統(tǒng)〕在堆內(nèi)存中搜索可用的足夠大小的空間,如果沒有足夠大小的空間〔可能是由于內(nèi)存碎片太多〕,就有可能調(diào)用系統(tǒng)功能去增加程序數(shù)據(jù)段的內(nèi)存空間,這樣就有機(jī)會分到足夠大小的內(nèi)存,然后進(jìn)行返回。顯然,堆的效率比棧要低得多。從這里我們可以看到,堆和棧相比,由于大量new/delete的使用,容易造成大量的內(nèi)存碎片;由于沒有專門的系統(tǒng)支持,效率很低;由于可能引發(fā)用戶態(tài)和核心態(tài)的切換,內(nèi)存的申請,代價變得更加昂貴。所以棧在程序中是應(yīng)用最廣泛的,就算是函數(shù)的調(diào)用也利用棧去完成,函數(shù)調(diào)用過程中的參數(shù),返回地址,EBP和局部變量都采用棧的方式存放。所以,我們推薦大家盡量用棧,而不是用堆。雖然棧有如此眾多的好處,但是由于和堆相比不是那么靈活,有時候分配大量的內(nèi)存空間,還是用堆好一些。無論是堆還是棧,都要防止越界現(xiàn)象的發(fā)生〔除非你是故意使其越界〕,因為越界的結(jié)果要么是程序崩潰,要么是摧毀程序的堆、棧結(jié)構(gòu),產(chǎn)生以想不到的結(jié)果,就算是在你的程序運(yùn)行過程中,沒有發(fā)生上面的問題,你還是要小心,說不定什么時候就崩掉,那時候debug可是相當(dāng)困難的:〕1、什么是const?

常類型是指使用類型修飾符const說明的類型,常類型的變量或?qū)ο蟮闹凳遣荒鼙桓碌?。〔?dāng)然,我們可以偷梁換柱進(jìn)行更新:〕2、為什么引入const?

const推出的初始目的,正是為了取代預(yù)編譯指令,消除它的缺點,同時繼承它的優(yōu)點。3、cons有什么主要的作用?

〔1〕可以定義const常量,具有不可變性。

例如:

constintMax=100;

intArray[Max];

〔2〕便于進(jìn)行類型檢查,使編譯器對處理內(nèi)容有更多了解,消除了一些隱患。

例如:

voidf<constinti>{}

編譯器就會知道i是一個常量,不允許修改;

〔3〕可以避免意義模糊的數(shù)字出現(xiàn),同樣可以很方便地進(jìn)行參數(shù)的調(diào)整和修改。

同宏定義一樣,可以做到不變則已,一變都變!如〔1〕中,如果想修改Max的內(nèi)容,只需要:constintMax=youwant;即可!

〔4〕可以保護(hù)被修飾的東西,防止意外的修改,增強(qiáng)程序的健壯性。

還是上面的例子,如果在函數(shù)體內(nèi)修改了i,編譯器就會報錯;

例如:

voidf<constinti>{i=10;//error!}

<5>為函數(shù)重載提供了一個參考。

classA

{

voidf<inti>

{}

file一個函數(shù)

voidf<inti>const{}

file上一個函數(shù)的重載

};

<6>可以節(jié)省空間,避免不必要的內(nèi)存分配。

例如:

#definePI3.14159

file常量宏

constdoulbe

Pi=3.14159;

file此時并未將Pi放入ROM中

doublei=Pi;

file此時為Pi分配內(nèi)存,以后不再分配!

doubleI=PI;

file編譯期間進(jìn)行宏替換,分配內(nèi)存

doublej=Pi;

file沒有內(nèi)存分配

doubleJ=PI;

file再進(jìn)行宏替換,又一次分配內(nèi)存!

const定義常量從匯編的角度來看,只是給出了對應(yīng)的內(nèi)存地址,而不是象#define一樣給出的是立即數(shù),所以,const定義的常量在程序運(yùn)行過程中只有一份拷貝,而#define定義的常量在內(nèi)存中有若干個拷貝。

〔7〕提高了效率。

編譯器通常不為普通const常量分配存儲空間,而是將它們保存在符號表中,這使得它成為一個編譯期間的常量,沒有了存儲與讀內(nèi)存的操作,使得它的效率也很高。3、如何使用const?

〔1〕修飾一般常量

一般常量是指簡單類型的常量。這種常量在定義時,修飾符const可以用在類型說明符前,也可以用在類型說明符后。

例如:

intconstx=2;或constintx=2;

〔2〕修飾常數(shù)組

定義或說明一個常數(shù)組可采用如下格式:

intconsta[5]={1,2,3,4,5};

constinta[5]={1,2,3,4,5};

〔3〕修飾常對象

常對象是指對象常量,定義格式如下:

classA;

constAa;

Aconsta;

定義常對象時,同樣要進(jìn)行初始化,并且該對象不能再被更新,修飾符const可以放在類名后面,也可以放在類名前面。

〔4〕修飾常指針

constint*A;

fileconst修飾指向的對象,A可變,A指向的對象不可變

intconst*A;

fileconst修飾指向的對象,A可變,A指向的對象不可變

int*constA;

fileconst修飾指針A,

A不可變,A指向的對象可變

constint*constA;

file指針A和A指向的對象都不可變

〔5〕修飾常引用

使用const修飾符也可以說明引用,被說明的引用為常引用,該引用所引用的對象不能被更新。其定義格式如下:

constdouble&v;

〔6〕修飾函數(shù)的常參數(shù)

const修飾符也可以修飾函數(shù)的傳遞參數(shù),格式如下:

voidFun<constintVar>;

告訴編譯器Var在函數(shù)體中的無法改變,從而防止了使用者的一些無意的或錯誤的修改。

〔7〕修飾函數(shù)的返回值:

const修飾符也可以修飾函數(shù)的返回值,是返回值不可被改變,格式如下:

constintFun1<>;

constMyClassFun2<>;

〔8〕修飾類的成員函數(shù):

const修飾符也可以修飾類的成員函數(shù),格式如下:

classClassName

{

public:

intFun<>const;

};

這樣,在調(diào)用函數(shù)Fun時就不能修改類里面的數(shù)據(jù)

〔9〕在另一連接文件中引用const常量

externconstinti;

file正確的引用

externconstintj=10;

file錯誤!常量不可以被再次賦值

另外,還要注意,常量必須初始化!

例如:

constinti=5;

4、幾點值得討論的地方:

〔1〕const究竟意味著什么?

說了這么多,你認(rèn)為const意味著什么?一種修飾符?接口抽象?一種新類型?

也許都是,在Stroustup最初引入這個關(guān)鍵字時,只是為對象放入ROM做出了一種可能,對于const對象,C++既允許對其進(jìn)行靜態(tài)初始化,也允許對他進(jìn)行動態(tài)初始化。理想的const對象應(yīng)該在其構(gòu)造函數(shù)完成之前都是可寫的,在析夠函數(shù)執(zhí)行開始后也都是可寫的,換句話說,const對象具有從構(gòu)造函數(shù)完成到析夠函數(shù)執(zhí)行之前的不變性,如果違反了這條規(guī)則,結(jié)果都是未定義的!雖然我們把const放入ROM中,但這并不能夠保證const的任何形式的墮落,我們后面會給出具體的辦法。無論const對象被放入ROM中,還是通過存儲保護(hù)機(jī)制加以保護(hù),都只能保證,對于用戶而言這個對象沒有改變。換句話說,廢料收集器〔我們以后會詳細(xì)討論,這就一筆帶過〕或數(shù)據(jù)庫系統(tǒng)對一個const的修改怎沒有任何問題。

〔2〕位元constV.S.抽象const?

對于關(guān)鍵字const的解釋有好幾種方式,最常見的就是位元const和抽象const。下面我們看一個例子:

classA

{

public:

Af<constA&a>;

};

如果采用抽象const進(jìn)行解釋,那就是f函數(shù)不會去改變所引用對象的抽象值,如果采用位元const進(jìn)行解釋,那就成了f函數(shù)不會去改變所引用對象的任何位元。

我們可以看到位元解釋正是c++對const問題的定義,const成員函數(shù)不被允許修改它所在對象的任何一個數(shù)據(jù)成員。

為什么這樣呢?因為使用位元const有2個好處:

最大的好處是可以很容易地檢測到違反位元const規(guī)定的事件:編譯器只用去尋找有沒有對數(shù)據(jù)成員的賦值就可以了。另外,如果我們采用了位元const,那么,對于一些比較簡單的const對象,我們就可以把它安全的放入ROM中,對于一些程序而言,這無疑是一個很重要的優(yōu)化方式?!碴P(guān)于優(yōu)化處理,我們到時候?qū)iT進(jìn)行討論〕

當(dāng)然,位元const也有缺點,要不然,抽象const也就沒有產(chǎn)生的必要了。

首先,位元const的抽象性比抽象const的級別更低!實際上,大家都知道,一個庫接口的抽象性級別越低,使用這個庫就越困難。

其次,使用位元const的庫接口會暴露庫的一些實現(xiàn)細(xì)節(jié),而這往往會帶來一些負(fù)面效應(yīng)。所以,在庫接口和程序?qū)崿F(xiàn)細(xì)節(jié)上,我們都應(yīng)該采用抽象const。

有時,我們可能希望對const做出一些其它的解釋,那么,就要注意了,目前,大多數(shù)對const的解釋都是類型不安全的,這里我們就不舉例子了,你可以自己考慮一下,總之,我們盡量避免對const的重新解釋。

〔3〕放在類內(nèi)部的常量有什么限制?

看看下面這個例子:

classA

{

private:

constintc3=7;

//???

staticintc4=7;

//???

staticconstfloatc5=7;

//???

};

你認(rèn)為上面的3句對嗎?呵呵,都不對!使用這種類內(nèi)部的初始化語法的時候,常量必須是被一個常量表達(dá)式初始化的整型或枚舉類型,而且必須是static和const形式。這顯然是一個很嚴(yán)重的限制!

那么,我們的標(biāo)準(zhǔn)委員會為什么做這樣的規(guī)定呢?一般來說,類在一個頭文件中被聲明,而頭文件被包含到許多互相調(diào)用的單元去。但是,為了避免復(fù)雜的編譯器規(guī)則,C++要求每一個對象只有一個單獨(dú)的定義。如果C++允許在類內(nèi)部定義一個和對象一樣占據(jù)內(nèi)存的實體的話,這種規(guī)則就被破壞了。

〔4〕如何初始化類內(nèi)部的常量?

一種方法就是static和const并用,在內(nèi)部初始化,如上面的例子;

另一個很常見的方法就是初始化列表:

classA

{

public:

A<inti=0>:test<i>{}

private:

constinti;

};

還有一種方式就是在外部初始化,例如:

classA

{

public:

A<>{}

private:

staticconstinti;

file注意必須是靜態(tài)的!

};

constintA::i=3;

〔5〕常量與數(shù)組的組合有什么特殊嗎?

我們給出下面的代碼:

constintsize[3]={10,20,50};

intarray[size[2]];

有什么問題嗎?對了,編譯通不過!為什么呢?

const可以用于集合,但編譯器不能把一個集合存放在它的符號表里,所以必須分配內(nèi)存。在這種情況下,const意味著"不能改變的一塊存儲"。然而,其值在編譯時不能被使用,因為編譯器在編譯時不需要知道存儲的內(nèi)容。自然,作為數(shù)組的大小就不行了:〕

你再看看下面的例子:

classA

{

public:

A<inti=0>:test[2]<{1,2}>{}

file你認(rèn)為行嗎?

private:

constinttest[2];

};

vc6下編譯通不過,為什么呢?

關(guān)于這個問題,前些時間,noy問我是怎么回事?我反問他:"你認(rèn)為呢?"他想了想,給出了一下解釋,大家可以看看:我們知道編譯器堆初始化列表的操作是在構(gòu)造函數(shù)之內(nèi),顯式調(diào)用可用代碼之前,初始化的次序依據(jù)數(shù)據(jù)聲明的次序。初始化時機(jī)應(yīng)該沒有什么問題,那么就只有是編譯器對數(shù)組做了什么手腳!其實做什么手腳,我也不知道,我只好對他進(jìn)行猜測:編譯器搜索到test發(fā)現(xiàn)是一個非靜態(tài)的數(shù)組,于是,為他分配內(nèi)存空間,這里需要注意了,它應(yīng)該是一下分配完,并非先分配test[0],然后利用初始化列表初始化,再分配test[1],這就導(dǎo)致數(shù)組的初始化實際上是賦值!然而,常量不允許賦值,所以無法通過。

呵呵,看了這一段冠冕堂皇的話,真讓我笑死了!noy別怪我揭你短呀:〕我對此的解釋是這樣的:C++標(biāo)準(zhǔn)有一個規(guī)定,不允許無序?qū)ο笤陬悆?nèi)部初始化,數(shù)組顯然是一個無序的,所以這樣的初始化是錯誤的!對于他,只能在類的外部進(jìn)行初始化,如果想讓它通過,只需要聲明為靜態(tài)的,然后初始化。

這里我們看到,常量與數(shù)組的組合沒有什么特殊!一切都是數(shù)組惹的禍!

〔6〕this指針是不是const類型的?

this指針是一個很重要的概念,那該如何理解她呢?也許這個話題太大了,那我們縮小一些:this指針是個什么類型的?這要看具體情況:如果在非const成員函數(shù)中,this指針只是一個類類型的;如果在const成員函數(shù)中,this指針是一個const類類型的;如果在volatile成員函數(shù)中,this指針就是一個volatile類類型的。

〔7〕const到底是不是一個重載的參考對象?

先看一下下面的例子:

classA

{

voidf<inti>

{}

file一個函數(shù)

voidf<inti>const{}

file上一個函數(shù)的重載

};

上面是重載是沒有問題的了,那么下面的呢?

classA

{

voidf<inti>

{}

file一個函數(shù)

voidf<constinti>{}

file?????

};

這個是錯誤的,編譯通不過。那么是不是說明內(nèi)部參數(shù)的const不予重載呢?再看下面的例子:

classA

{

voidf<int&>

{}

file一個函數(shù)

voidf<constint&>{}

file?????

};

這個程序是正確的,看來上面的結(jié)論是錯誤的。為什么會這樣呢?這要涉與到接口的透明度問題。按值傳遞時,對用戶而言,這是透明的,用戶不知道函數(shù)對形參做了什么手腳,在這種情況下進(jìn)行重載是沒有意義的,所以規(guī)定不能重載!當(dāng)指針或引用被引入時,用戶就會對函數(shù)的操作有了一定的了解,不再是透明的了,這時重載是有意義的,所以規(guī)定可以重載。

〔8〕什么情況下為const分配內(nèi)存?

以下是我想到的可能情況,當(dāng)然,有的編譯器進(jìn)行了優(yōu)化,可能不分配內(nèi)存。

A、作為非靜態(tài)的類成員時;

B、用于集合時;

C、被取地址時;

D、在main函數(shù)體內(nèi)部通過函數(shù)來獲得值時;

E、const的class或struct有用戶定義的構(gòu)造函數(shù)、析構(gòu)函數(shù)或基類時;。

F、當(dāng)const的長度比計算機(jī)字長還長時;

G、參數(shù)中的const;

H、使用了extern時。

不知道還有沒有其他情況,歡迎高手指點:〕

〔9〕臨時變量到底是不是常量?

很多情況下,編譯器必須建立臨時對象。像其他任何對象一樣,它們需要存儲空間而且必須被構(gòu)造和刪除。區(qū)別是我們從來看不到編譯器負(fù)責(zé)決定它們的去留以與它們存在的細(xì)節(jié)。對于C++標(biāo)準(zhǔn)草案而言:臨時對象自動地成為常量。因為我們通常接觸不到臨時對象,不能使用與之相關(guān)的信息,所以告訴臨時對象做一些改變有可能會出錯。當(dāng)然,這與編譯器有關(guān),例如:vc6、vc7都對此作了擴(kuò)展,所以,用臨時對象做左值,編譯器并沒有報錯。

〔10〕與static搭配會不會有問題?

假設(shè)有一個類:

classA

{

public:

staticvoidf<>const{}

};

我們發(fā)現(xiàn)編譯器會報錯,因為在這種情況下static不能夠與const共存!

為什么呢?因為static沒有this指針,但是const修飾this指針,所以...

〔11〕如何修改常量?

有時候我們卻不得不對類內(nèi)的數(shù)據(jù)進(jìn)行修改,但是我們的接口卻被聲明了const,那該怎么處理呢?我對這個問題的看法如下:

1〕標(biāo)準(zhǔn)用法:mutable

classA

{

public:

A<inti=0>:test<i>

{}

voidSetValue<inti>const{test=i;}

private:

mutableinttest;

file這里處理!

};

2〕強(qiáng)制轉(zhuǎn)換:const_cast

classA

{

public:

A<inti=0>:test<i>

{}

voidSetValue<inti>const

{const_cast<int><test>=i;}//這里處理!

private:

inttest;

};

3〕靈活的指針:int*

classA

{

public:

A<inti=0>:test<i>

{}

voidSetValue<inti>const

{*test=i;}

private:

int*test;

file這里處理!

};

4〕未定義的處理

classA

{

public:

A<inti=0>:test<i>

{}

voidSetValue<inti>const

{int*p=<int*>&test;*p=i;}//這里處理!

private:

inttest;

};

注意,這里雖然說可以這樣修改,但結(jié)果是未定義的,避免使用!

5〕內(nèi)部處理:this指針

classA

{

public:

A<inti=0>:test<i>

{}

voidSetValue<inti>const

{<<A*>this>->test=i;}//這里處理!

private:

inttest;

};

6〕最另類的處理:空間布局

classA

{

public:

A<inti=0>:test<i>,c<'a'>{

}

private:

charc;

constinttest;

};

intmain<>

{

Aa<3>;

A*pa=&a;

char*p=<char*>pa;

int*

pi=<int*><p+4〕;//利用邊緣調(diào)整

*pi=5;

file此處改變了test的值!

return0;

}

雖然我給出了6中方法,但是我只是想說明如何更改,但出了第一種用法之外,另外5種用法,我們并不提倡,不要因為我這么寫了,你就這么用,否則,我真是要誤人子弟了:〕

〔12〕最后我們來討論一下常量對象的動態(tài)創(chuàng)建。

既然編譯器可以動態(tài)初始化常量,就自然可以動態(tài)創(chuàng)建,例如:

constint*pi=newconstint<10>;

這里要注意2點:

1〕const對象必須被初始化!所以<10>是不能夠少的。

2〕new返回的指針必須是const類型的。

那么我們可不可以動態(tài)創(chuàng)建一個數(shù)組呢?

答案是否定的,因為new內(nèi)置類型的數(shù)組,不能被初始化。

這里我們忽視了數(shù)組是類類型的,同樣對于類內(nèi)部數(shù)組初始化我們也做出了這樣的忽視,因為這涉與到數(shù)組的問題,我們以后再討論。

淺析C++里面的宏收藏說到宏,恐怕大家都能說出點東西來:一種預(yù)處理,沒有分號〔真的嗎?〕。然后呢?

嗯茫然中

好吧,我們就從這開始說起。

最常見的宏恐怕是#include了,其次就是#define還有

還是從宏的用途分類吧:1、#include主要用于包含引用文件,至今其地位無人能替代;2、注釋掉代碼。例如:

#if0

#endif;

這種機(jī)制是目前注釋掉代碼的最佳選擇,為摩托羅拉公司員工所普遍采用;3、代碼版本管理。例如:

#ifdefDEBUG

file調(diào)試版本

#else

file非調(diào)試版本

#endif;4、聲明宏。例如:

#defineDECLARE_MESSAGE<x>x<>;~x<>

file有沒有分號?哈哈

//

classA

{

public:

DECLARE_MESSAGE<A>;

}

想起什么了,呵呵:〕對,VC里面有好多這樣的東東,有空我會寫《我的VC歷程》,到

時候會把VC里的各種宏詳細(xì)的解釋一下,那可是一個龐大的工程:〕5、符號常量。例如:

#definePI3.141596、內(nèi)聯(lián)函數(shù)。例如:

#defineCLEAR<x><<x>=0>7、泛型函數(shù)。例如:

#defineABS<x><<x>>0?<x>:-<x>>

x=3沒問題!x=1.3也沒問題!

如果是這樣呢:

#include<iostream.h>

#defineA<x><<x>>0?<x>:-<x>>

voidmain<>

{

inti=-1;

cout<<A<1><<endl;

cout<<A<++i><<endl;

}

有問題了,不過以后再說,大概講constorinline時會說的:〕8、泛型類型。例如:

#defineStack<T>

Stack__##T

#defineStackdeclare<T>

classStack<T>{}

Stackdeclare<int>;

Stackdeclare<char>;

Stack<int>s1;

Stack<char>s2;9、語法擴(kuò)展。例如:

Set<int>s;//假設(shè)Set為一個描述集合的類

inti;

FORALL<i,s>;

宏最大的問題便是易引起沖突,例如:

libA.h:

#defineMACROstuff

同時:

libB.h:

#defineMACROstuff

下面我們對他們進(jìn)行引用:

user.cpp:

#include"libA.h"

#include"libB.h"

糟糕,出現(xiàn)了重定義!

還有一種沖突的可能:

libB.h:〔沒有定義宏MACRO>

classx

{voidMACRO<>;

};

那么程序運(yùn)行期間,libA.h中的宏講會改變libB.h中的成員函數(shù)的名字,導(dǎo)致不可預(yù)料

的結(jié)果。

宏的另一個問題,便是如7中出現(xiàn)的問題,如果你把7中的x設(shè)為'a',

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論