C++中動(dòng)態(tài)內(nèi)存分配的示例分析-創(chuàng)新互聯(lián)

這篇文章主要介紹了C++中動(dòng)態(tài)內(nèi)存分配的示例分析,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

員工經(jīng)過(guò)長(zhǎng)期磨合與沉淀,具備了協(xié)作精神,得以通過(guò)團(tuán)隊(duì)的力量開發(fā)出優(yōu)質(zhì)的產(chǎn)品。創(chuàng)新互聯(lián)堅(jiān)持“專注、創(chuàng)新、易用”的產(chǎn)品理念,因?yàn)椤皩W⑺詫I(yè)、創(chuàng)新互聯(lián)網(wǎng)站所以易用所以簡(jiǎn)單”。公司專注于為企業(yè)提供成都做網(wǎng)站、網(wǎng)站設(shè)計(jì)、微信公眾號(hào)開發(fā)、電商網(wǎng)站開發(fā),小程序設(shè)計(jì),軟件按需求定制網(wǎng)站等一站式互聯(lián)網(wǎng)企業(yè)服務(wù)。

C++動(dòng)態(tài)內(nèi)存分配

為了解決這個(gè)普通的編程問(wèn)題,在運(yùn)行時(shí)能創(chuàng)建和銷毀對(duì)象是基本的要求。當(dāng)然,C已提供了動(dòng)態(tài)內(nèi)存分配函數(shù)malloc( )和free( ),以及malloc( )的變種(realloc:改變分配內(nèi)存的大小,calloc:指針指向內(nèi)存前初始化),這些函數(shù)在運(yùn)行時(shí)從堆中(也稱自由內(nèi)存)分配存儲(chǔ)單元,但是運(yùn)用這些庫(kù)函數(shù)需要計(jì)算需要開辟內(nèi)存的大小,容易出現(xiàn)錯(cuò)誤。

    那么通常我們?cè)贑語(yǔ)言中我們開辟內(nèi)存的方式如下:

(void*)malloc(sizeof(void));

然而,在C+ +中這些函數(shù)不能很好地運(yùn)行。構(gòu)造函數(shù)不允許通過(guò)向?qū)ο髠鬟f內(nèi)存地址來(lái)初始化它。如果那么做了,我們可能

  • 忘記了。則對(duì)象初始化在C + +中難以保證。

  • 期望某事發(fā)生,但結(jié)果是在給對(duì)象初始化之前意外地對(duì)對(duì)象作了某種改變。

  • 把錯(cuò)誤規(guī)模的對(duì)象傳遞給了它。

當(dāng)然,即使我們把每件事都做得很正確,修改我們的程序的人也容易犯同樣的錯(cuò)誤。不正確的初始化是編程出錯(cuò)的主要原因,所以在堆上創(chuàng)建對(duì)象時(shí),確保構(gòu)造函數(shù)調(diào)用是特別重要的。

C+ +是如何保證正確的初始化和清理并允許我們?cè)诙焉蟿?dòng)態(tài)創(chuàng)建對(duì)象的呢?

   答案是使動(dòng)態(tài)對(duì)象創(chuàng)建成為語(yǔ)言的核心。malloc( )和free( )是庫(kù)函數(shù),因此不在編譯器控制范圍之內(nèi)。如果我們有一個(gè)能完成動(dòng)態(tài)內(nèi)存分配及初始化工作的運(yùn)算符和另一個(gè)能完成清理及釋放內(nèi)存工作的運(yùn)算符,編譯器就可以保證所有對(duì)象的構(gòu)造函數(shù)和析構(gòu)函數(shù)都會(huì)被調(diào)用。

   若使用原始的動(dòng)態(tài)內(nèi)存開辟方式就會(huì)顯得很繁瑣,具體代碼如下:

#include<cstdlib> 
#include<cstring> 
#include<iostream> 
using namespace std; 
class Obj 
{ 
 int i,j,k; 
 enum {sz=100}; 
 char buf[sz]; 
public: 
  void initialize() 
  { 
    cout<<"initialize"<<endl; 
    i=k=j=0; 
    memset(buf,0,sz); 
  } 
  void destroy() const 
  { 
   cout<<"destroying Obj"<<endl; 
  } 
}; 
int main() 
{ 
  Obj* obj=(Obj*)malloc(sizeof(Obj)); 
   if(obj!=0) 
  obj->initialize(); 
  obj->destroy(); 
  free(obj); 
 return 0; 
}

    在上面這行代碼中,我們可以看到使用malloc( )為對(duì)象分配內(nèi)存:obj* Obj = (obj*)malloc(sizeof(obj)) ;
這里用戶必須決定對(duì)象的長(zhǎng)度(這也是程序出錯(cuò)原因之一)。因?yàn)樗且粔K內(nèi)存而不是一個(gè)對(duì)象,所以malloc( )返回一個(gè)void*.C++不允許將一個(gè)void* 賦予任何指針,所以必須映射。因?yàn)閙alloc( )可能找不到可分配的內(nèi)存(在這種情況下它返回 0),所以必須檢查返回的指針以確信內(nèi)存分配成功。

但最壞的是:Obj->initialize( ) ;用戶在使用對(duì)象之前必須記住對(duì)它初始化。注意構(gòu)造函數(shù)沒有被使用,因?yàn)闃?gòu)造函數(shù)不能被顯式地調(diào)用—而是當(dāng)對(duì)象創(chuàng)建時(shí)由編譯器調(diào)用。這里的問(wèn)題是現(xiàn)在用戶可能在使用對(duì)象時(shí)忘記執(zhí)行初始化,因此這也是引入程序缺陷的主要來(lái)源。許多程序設(shè)計(jì)者發(fā)現(xiàn) C的動(dòng)態(tài)內(nèi)存分配函數(shù)太復(fù)雜,令人混淆。所以, C程序設(shè)計(jì)者常常在靜態(tài)內(nèi)存區(qū)域使用虛擬內(nèi)存機(jī)制分配很大的變量數(shù)組以避免使用動(dòng)態(tài)內(nèi)存分配。因?yàn)镃++能讓一般的程序員安全使用庫(kù)函數(shù)而不費(fèi)力,所以應(yīng)當(dāng)避免使用 C的動(dòng)態(tài)內(nèi)存方法。C++中的解決方案是把創(chuàng)建一個(gè)對(duì)象所需的所有動(dòng)作都結(jié)合在一個(gè)稱為new的運(yùn)算符里。當(dāng)用new(new的表達(dá)式)創(chuàng)建一個(gè)對(duì)象時(shí),它就在堆里為對(duì)象分配內(nèi)存并為這塊內(nèi)存調(diào)用構(gòu)造函數(shù)。

   因此,如果我們寫出下面的表達(dá)式foo *fp = new foo(1,2) ; 在運(yùn)行時(shí)等價(jià)于調(diào)用malloc(sizeof(foo)),并使用(1,2)作為參數(shù)表來(lái)為

foo調(diào)用構(gòu)造函數(shù),返回值作為this指針的結(jié)果地址。在該指針被賦給 fp之前,它是不定的、未初始化的對(duì)象— 在這之前我們甚至不能觸及它。它自動(dòng)地被賦予正確的 foo類型,所以不必進(jìn)行映射。缺省的new還檢查以確信在傳遞地址給構(gòu)造函數(shù)之前內(nèi)存分配是成功的,所以我們不必顯式地確定調(diào)用是否成功。在本章后面,我們將會(huì)發(fā)現(xiàn),如果沒有可供分配的內(nèi)存會(huì)發(fā)生什么事情。我們可以為類使用任何可用的構(gòu)造函數(shù)而寫一個(gè) ne w表達(dá)式。如果構(gòu)造函數(shù)沒有參數(shù),可以寫沒有構(gòu)造函數(shù)參數(shù)表的new表達(dá)式:

foo *fp = new foo ;我們已經(jīng)注意到了,在堆里創(chuàng)建對(duì)象的過(guò)程變得簡(jiǎn)單了—只是一個(gè)簡(jiǎn)單的表達(dá)式 ,它帶有內(nèi)置的長(zhǎng)度計(jì)算、類型轉(zhuǎn)換和安全檢查。這樣在堆里創(chuàng)建一個(gè)對(duì)象和在棧里創(chuàng)建一個(gè)對(duì)象一樣容易。

new表達(dá)式的反面是delete表達(dá)式。delete表達(dá)式首先調(diào)用析構(gòu)函數(shù),然后釋放內(nèi)存(經(jīng)常是調(diào)用free( ))。正如new表達(dá)式返回一個(gè)指向?qū)ο蟮闹羔樢粯?delete表達(dá)式需要一個(gè)對(duì)象的地址。delete fp ;上面的表達(dá)式清除了早先創(chuàng)建的動(dòng)態(tài)分配的對(duì)象foo。delete只用于刪除由new創(chuàng)建的對(duì)象。如果用malloc( )(或calloc( )或realloc( ))創(chuàng)建一個(gè)對(duì)象,然后用delete刪除它,這個(gè)行為是未定義的。因?yàn)榇蠖鄶?shù)缺省的new和delete實(shí)現(xiàn)機(jī)制都使

用了malloc( )和free( ),所以我們很可能會(huì)沒有調(diào)用析構(gòu)函數(shù)就釋放了內(nèi)存。如果正在刪除的對(duì)象指針是 0,將不發(fā)生任何事情。為此,建議在刪除指針后立即把指針賦值為0以免對(duì)它刪除兩次。對(duì)一個(gè)對(duì)象刪除兩次一定不是一件好事,這會(huì)引起問(wèn)題。

當(dāng)創(chuàng)建一個(gè)new表達(dá)式時(shí)有兩件事發(fā)生。首先,使用運(yùn)算符new分配內(nèi)存,然后調(diào)用構(gòu)造函數(shù)。在delete表達(dá)式里,調(diào)用析構(gòu)函數(shù),然后使用運(yùn)算符 delete釋放內(nèi)存。我們永遠(yuǎn)無(wú)法控制構(gòu)造函數(shù)和析構(gòu)函數(shù)的調(diào)用(否則我們可能意外地?cái)噥y它們),但可以改變內(nèi)存分配函數(shù)運(yùn)算  符new和delete。被new和delete使用的內(nèi)存分配系統(tǒng)是為通用目的而設(shè)計(jì)的。但在特殊的情形下,它不能滿足我們的需要。改變分配系統(tǒng)的原因是考慮效率:我們也許要?jiǎng)?chuàng)建和銷毀一個(gè)特定的類的非常多的對(duì)象以至于這個(gè)運(yùn)算變成了速度的瓶頸。 C++允許重載new和delete來(lái)實(shí)現(xiàn)我們自己的存儲(chǔ)分配方案,所以可以像這樣處理問(wèn)題。

另外一個(gè)問(wèn)題是堆碎片:分配不同大小的內(nèi)存可能造成在堆上產(chǎn)生很多碎片,以至于很快用完內(nèi)存。也就是內(nèi)存可能還有,但由于是碎片,找不到足夠大的內(nèi)存滿足我們的需要。通過(guò)為特定類創(chuàng)建我們自己的內(nèi)存分配器,可以確保這種情況不會(huì)發(fā)生。 
在嵌入和實(shí)時(shí)系統(tǒng)里,程序可能必須在有限的資源情況下運(yùn)行很長(zhǎng)時(shí)間。這樣的系統(tǒng)也可能要求分配內(nèi)存花費(fèi)相同的時(shí)間且不允許出現(xiàn)堆內(nèi)存耗盡或出現(xiàn)很多碎片的情況。由客戶定制的內(nèi)存分配器是一種解決辦法,否則程序設(shè)計(jì)者在這種情況下要避免使用new和delete,從而失去了C + +很有價(jià)值的優(yōu)點(diǎn)。 

當(dāng)重載運(yùn)算符new和delete時(shí),記住只改變?cè)械膬?nèi)存分配方法是很重要的。編譯器將用new代替缺省的版本去分配內(nèi)存,然后為那個(gè)內(nèi)存調(diào)用構(gòu)造函數(shù)。所以,雖然編譯器遇到new 時(shí)會(huì)分配內(nèi)存并調(diào)用構(gòu)造函數(shù),但當(dāng)我們重載new時(shí),可以改變的只是內(nèi)存分配部分。(delete 也有相似的限制。)

當(dāng)重載運(yùn)算符new時(shí),也可以替換它用完內(nèi)存時(shí)的行為,所以必須在運(yùn)算符new里決定做什么:返回0、寫一個(gè)調(diào)用new - handler的循環(huán)、再試著分配或用一個(gè) bad_alloc異常處理重載new和delete與重載任何其他運(yùn)算符一樣。但可以選擇重載全局內(nèi)存分配函數(shù),或?yàn)樘囟ǖ念愂褂锰囟ǖ姆峙浜瘮?shù)

   當(dāng)全局版本的new和delete不能滿足整個(gè)系統(tǒng)時(shí),對(duì)其重載是很極端的方法。如果重載全局版本,那么缺省版本就完全不能被訪問(wèn)—甚至在這個(gè)重載定義里也不能調(diào)用它們。

重載的ne w 必須有一個(gè)size_t 參數(shù)。這個(gè)參數(shù)由編譯器產(chǎn)生并傳遞給我們,它是要分配內(nèi)存的對(duì)象的長(zhǎng)度。必須返回一個(gè)指向等于這個(gè)長(zhǎng)度(或大于這個(gè)長(zhǎng)度,如果我們有這樣做的原因)的對(duì)象的指針,或如果沒有找到存儲(chǔ)單元(在這種情況下,構(gòu)造函數(shù)不被調(diào)用),返回一個(gè)0。然而如果找不到存儲(chǔ)單元,不能僅僅返回0,還應(yīng)該調(diào)用new-handler或進(jìn)行異常處理,通知這里存在問(wèn)題。

運(yùn)算符new的返回值是一個(gè)void *,而不是指向任何特定類型的指針。它所做的是分配內(nèi)存,而不是完成一個(gè)對(duì)象的建立—直到構(gòu)造函數(shù)調(diào)用了才完成對(duì)象的創(chuàng)建,這是由編譯器所確保的動(dòng)作,不在我們的控制范圍內(nèi)。

運(yùn)算符delete接受一個(gè)指向由運(yùn)算符new分配的內(nèi)存的void *。它是一個(gè)void *因?yàn)樗窃谡{(diào)用析構(gòu)函數(shù)后得到的指針。析構(gòu)函數(shù)從存儲(chǔ)單元里移去對(duì)象。運(yùn)算符 delete的返回類型是void。

下面提供了一個(gè)如何重載全局new和delete的簡(jiǎn)單的例子:

#include <stdlib.h> 
 
void * operator new(size_t sz) 
{ 
  printf("operator new:%d bytes\n",sz); 
  void* m=malloc(sz); 
  if(!m) puts("out of memory"); 
  return 0; 
} 
void operator delete(void* m) 
{ 
puts("operator delete"); 
free(m); 
} 
class s 
{ 
  int i[100]; 
public: 
  s(){puts("s::s()");} 
  ~s(){puts("s::~s()");} 
}; 
int main() 
{ 
 puts("creating & destorying an int "); 
 int* p=new int(47); 
 delete p; 
 puts("creating & destorying an s"); 
 s* S=new s; 
 delete S; 
 puts("creating & destorying an s[3]"); 
 s* SA=new s[3]; 
 delete [] SA; 
}

   這里可以看到重載new和delete的一般形式。為了實(shí)現(xiàn)內(nèi)存分配器,使用了標(biāo)準(zhǔn) C庫(kù)函數(shù) malloc( )和free( )(可能缺省的new和delete也使用這些函數(shù))。它們還打印出了它們正在做什么的信息。注意,這里使用 printf( )和puts( )而不是i o s t r e a m s。當(dāng)創(chuàng)建了一個(gè)i o s t r e a m對(duì)象時(shí)(像全局的c i n、c o u t和c e r r),它們調(diào)用new去分配內(nèi)存。用printf( )不會(huì)進(jìn)入死鎖狀態(tài),因?yàn)樗徽{(diào)用new來(lái)初始化本身。

在main( )里,創(chuàng)建內(nèi)部數(shù)據(jù)類型的對(duì)象以證明在這種情況下重載的new和delete也被調(diào)用。然后創(chuàng)建一個(gè)類型s的單個(gè)對(duì)象,接著創(chuàng)建一個(gè)數(shù)組。對(duì)于數(shù)組,我們可以看到需要額外的內(nèi)存用于存放數(shù)組對(duì)象數(shù)量的信息。在所有情況里,都是使用全局重載版本的new和delete。

     為一個(gè)類重載new和delete時(shí),不必明說(shuō)是 static,我們?nèi)允窃趧?chuàng)建 static成員函數(shù)。它的語(yǔ)法也和重載任何其他運(yùn)算符一樣。當(dāng)編譯器看到使用new創(chuàng)建類對(duì)象時(shí),它選擇成員版本運(yùn)算符new而不是全局版本的new。但全局版本的new和delete為所有其他類型對(duì)象使用(除非它們有自己的new和delete)。 

       如果為一個(gè)類重載了運(yùn)算符new和delete,那么無(wú)論何時(shí)創(chuàng)建這個(gè)類的一個(gè)對(duì)象都將調(diào)用這些運(yùn)算符。但如果為這些對(duì)象創(chuàng)建一個(gè)數(shù)組時(shí),將調(diào)用全局運(yùn)算符new( )立即為這個(gè)數(shù)組分配足夠的內(nèi)存。全局運(yùn)算符 delete( )被調(diào)用來(lái)釋放這塊內(nèi)存??梢酝ㄟ^(guò)為那個(gè)類重載數(shù)組版本的運(yùn)算符new [ ]和delete [ ]來(lái)控制對(duì)象數(shù)組的內(nèi)存分配。這里提供了一個(gè)顯示兩個(gè)不同版本被調(diào)用的例子:?這里,全局版本的new和delete被調(diào)用,除了加入了跟蹤信息以外,它們和未重載版本new 和delete的效果是一樣的。當(dāng)然,我們可以在重載的new和delete里使用想要的內(nèi)存分配方案。

        可以看到除了加了一個(gè)括號(hào)外,數(shù)組版本的new和delete與單個(gè)對(duì)象版本是一樣的。在這兩種情況下,要傳遞分配的對(duì)象內(nèi)存大小。傳遞給數(shù)組版本的內(nèi)存大小是整個(gè)數(shù)組的大小。應(yīng)該記住重載運(yùn)算符new唯一需要做的是返回指向一個(gè)足夠大的內(nèi)存的指針。雖然我們可以初始化那塊內(nèi)存,但通常這是構(gòu)造函數(shù)的工作,構(gòu)造函數(shù)將被編譯器自動(dòng)調(diào)用。 

感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“C++中動(dòng)態(tài)內(nèi)存分配的示例分析”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持創(chuàng)新互聯(lián)建站,關(guān)注創(chuàng)新互聯(lián)網(wǎng)站建設(shè)公司行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來(lái)學(xué)習(xí)!

另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)建站muchs.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。

本文名稱:C++中動(dòng)態(tài)內(nèi)存分配的示例分析-創(chuàng)新互聯(lián)
標(biāo)題路徑:http://muchs.cn/article4/ipdoe.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制網(wǎng)站、自適應(yīng)網(wǎng)站定制開發(fā)、電子商務(wù)、靜態(tài)網(wǎng)站、網(wǎng)站營(yíng)銷

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站建設(shè)