青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

海納百川

 

new/delete 操作符

[TC++PL] new/delete 操作符
 
 
Cpp Operators of new and delete
1. 動態(tài)內(nèi)存分配與釋放(new and delete)
一般說來,一個對象的生命期是由它被創(chuàng)建時所處的區(qū)域決定的。例如,在一對{}類定義的一個對象,在離開這個由{}所界定的區(qū)域時,該對象就會被銷毀,在這個區(qū)域之外這個對象是不存在的,程序的其他部分不能再引用這個對象了。

 

如果希望在離開了創(chuàng)建這個對象時所處的區(qū)域后,還希望這個對象存在并能繼續(xù)引用它,就必須用new操作符在自由存儲空間來分配一個對象。這個過程也叫做動態(tài)內(nèi)存分配,也叫堆對象。任何由new操作符分配的對象都應(yīng)該用delete操作符手動地銷毀掉。C++標(biāo)準(zhǔn)并沒有定義任何形式的“垃圾收集”機(jī)制。delete操作符只能用于由new返回的指針,或者是零。當(dāng)delete的參數(shù)是0時,不會產(chǎn)生任何效果,也就是說這個delete操作符對應(yīng)的函數(shù)根本就不會被執(zhí)行。

 

new(delete)既可以分配(釋放)單個的對象(當(dāng)然也包括內(nèi)建類型),也可以分配(釋放)對象數(shù)組。下面是其函數(shù)原型:
#include <new>

void* operator new(size_t); // 參數(shù)是單個對象的大小

void* operator new[](size_t); // 參數(shù)是對象數(shù)組的總的大小

void delete(void*);

void delete[](void*);

C++標(biāo)準(zhǔn)中并沒有要求new操作符對分配出來的空間進(jìn)行初始化。下面是使用new分配一個字符數(shù)組的例子:
char* save_string(const char* p)

{
char* s = new char[strlen(p)+1];

// ...

return s;

}

 

char* p = save_string(argv[1]);

// ...

delete[] p;

 


class X { /* ... */ }

X* p = new[10] X;

X* p2 = new X[10];

vector<int>* pv = new vector<int>(5);

在new分配出足夠的空間后,編譯器會緊接著調(diào)用X的缺省構(gòu)造函數(shù)對空間進(jìn)行初始化。注意,上述兩種形式都是可以的,無論X是內(nèi)建類型還是自定義用戶類型。對于類來說,還可以使用類的構(gòu)造函數(shù)形式,如上面創(chuàng)建vector類型對象的例子。此外,一個用非數(shù)組形式的new操作符創(chuàng)建的對象,不能用數(shù)組形式的delete操作符來銷毀。

2. 提供自己的內(nèi)存管理:重載new/delete操作符
我們可以為new/delete定義自己的內(nèi)存管理方式,但是替換全局的new/delete操作符的實現(xiàn)是不夠好的,原因很明顯:有些人可能需要缺省的new/delete操作的一些方面,而另一些人則可能完全使用另外一種版本的實現(xiàn)。所以最好的辦法是為某個特定的類提供它自己的內(nèi)存管理方式。

 

一個類的operator new()和operator delete()成員函數(shù),隱式地成為靜態(tài)成員函數(shù)。因此它們沒有this指針,也不能修改對象(很好理解,當(dāng)調(diào)用new的時候?qū)ο筮€沒有真正創(chuàng)建呢,當(dāng)然不能修改對象了!)。當(dāng)然在重載定義的時候,原型還是要與前面提到的一致。看下面這個例子:

void* Employee::operator new(size_t s)

{
// 分配s字節(jié)的內(nèi)存空間,并返回這個空間的地址

 

void Employee::operator delete(void* p, size_t s)

{
// 假定指針p是指向由Employee::operator new()分配的大小為s字節(jié)的內(nèi)存空間。

// 釋放這塊空間以供系統(tǒng)在以后重用。

}
任何一個operator new()的操作符定義,都以一個尺寸值作為第一個參數(shù),且待分配對象的大小隱式給定,其值就作為new操作符函數(shù)的第一個參數(shù)值。

 


在這里分配空間的具體實現(xiàn)可以是多種多樣的,可以直接使用malloc/free(缺省的全局new/delete大部分都是用的這種),可以在指定的內(nèi)存塊中分配空間(下節(jié)將要詳述),也可能還有其他的更好的更適合你的應(yīng)用的方式。

 

那么如何重載數(shù)組形式的new[]/delete[]操作符呢?與普通形式一樣,只不過delete[]的參數(shù)形式稍有不同,如下所示:
class Employee {

public:
void* operator new[](size_t);

void operator delete[](void*); // 單參數(shù)形式,少了一個size_t參數(shù)

void operator delete[](void*,size_t); //兩個參數(shù)形式也是可以的,但無必要

// ...

};

 

在編譯器的內(nèi)部實現(xiàn)中,傳入new/delete[]的尺寸值可能是數(shù)組的大小s加上一個delta。這個delta量是編譯器的內(nèi)部實現(xiàn)所定義的某種額外開銷。為什么delete操作符不需要第二個尺寸參數(shù)呢?因為這個數(shù)組的大小s以及delta量都由系統(tǒng)“記住”了。但是delete[]的兩個參數(shù)形式的原型也是可以聲明的,在調(diào)用的時候會把s*sizeof(SomeClass)+delta作為第二個參數(shù)值傳入。delta量是與編譯器實現(xiàn)相關(guān)的,因此對于用戶程序員來說是不必要知道的。故而這里只提供單參數(shù)版本就可以了。(這倒是提供了一種查看這個delta量的方法。根據(jù)實際測試,GCC 4.1采用了4個字節(jié)的delta量。)

 


到這里應(yīng)該注意到,當(dāng)我們調(diào)用operator delete()的時候,只給出了指針,并沒有給出對象大小的參數(shù)。那么編譯器是怎么知道應(yīng)該給operator delete()提供正確的尺寸值的呢?如果delete參數(shù)類型就是該對象的確切型別,那么這是一個簡單的事情,但是事情并不是總是這樣。看下面的例子:

class Manager : public Employee {

int level;

// ...

};
void f()

{
Employee* p = new Manager; // 麻煩:確切型別丟失了!

delete p;

}
這個時候編譯器不能得到正確的對象的尺寸。這就需要用戶的幫助了:只需要將基類的析構(gòu)函數(shù)聲明稱為虛函數(shù)即可。

 

3. 在指定位置安放對象(Placement of Objects)
new操作符的缺省方式是在自由內(nèi)存空間中創(chuàng)建對象。如果希望在指定的地方分配對象,就應(yīng)該使用這里介紹的方法。看下面的例子:
class X {

public:
X(int);
//...
};
當(dāng)需要把對象放置到指定地方的時候,只需要為分配函數(shù)提供一個額外的參數(shù)(既指定的某處內(nèi)存的地址),然后在使用new的時候提供這樣的一個額外參數(shù)即可。看下面的例子:
void* operator new(size_t, void* p) { return p; } // 顯示安放操作符

void* buf = reinterpret_cast<void*>(0xF00F); // 某個重要的地址

X* p2 = new(buf) X; // 在buf地址處創(chuàng)建一個X對象,

// 實際調(diào)用函數(shù)operator new(sizeof(X),buf)

 


4. 內(nèi)存分配失敗與new_handler
如果new操作符不能分配出內(nèi)存,會發(fā)生什么呢?默認(rèn)情況下,這個分配器會拋出一個bad_alloc異常對象。看下面的例子:
void f()

{
try{
for(;;) new char [10000];

}
catch(bad_alloc) {

cerr << "Memory exhausted!\n";

}
}
[疑問:構(gòu)造一個異常對象也需要內(nèi)存空間,既然已經(jīng)內(nèi)存耗盡了,那這個內(nèi)存又從哪里來呢?]
可以自定義內(nèi)存耗盡時的處理方法(new_handler)。當(dāng)new操作失敗時,首先會調(diào)用一個由set_new_handler()指定的函數(shù)。我們可以自定義這個函數(shù),然后用set_new_handler()來登記。最后當(dāng)new操作失敗時可以調(diào)用適當(dāng)?shù)淖远x處理過程。看下面的例子:
#include <new> // set_new_handler()原型在此頭文件中

void out_of_store()

{
cerr << "operator new failed: out of store\n";

throw bad_alloc();

}

 

set_new_handler(out_of_store);
for(;;) new char[10000];

cout << "done\n";

 


上述例子中控制流不會到達(dá)最后一句輸出,也就是說永遠(yuǎn)不會輸出done。而是會輸出:
operator new failed: out of store

自定義的new_handler函數(shù)的原型如下:
typedef void (*new_handler)();


5. 標(biāo)準(zhǔn)頭文件<new>中的原型
下面是標(biāo)準(zhǔn)頭文件中的各種原型聲明:
class bad_alloc : public exception { /* ... */ }

 


struct nothrow_t { };

extern struct nothrow_t nothrow; // 內(nèi)存分配器將不會拋出異常

 


typedef void (*new_handler)();

new_handler set_new_handler(new_handler new_p) throw();

 


(1)普通的內(nèi)存分配,失敗時拋出bad_alloc異常
// 單個對象的分配與釋放

void* operator new(size_t) throw(bad_alloc);

void operator delete(void*) throw();

// 對象數(shù)組分配與釋放

void* operator new[](size_t) throw(bad_alloc);

void operator delete[](void*) throw();

 


(2)與C方式兼容的內(nèi)存分配,失敗時返回0,不拋出異常
// 單個對象分配與釋放

void* operator new(size_t, const nothrow_t&) throw();

void operator delete(void*, const nothrow_t&) throw();

// 對象數(shù)組分配與釋放

void* operator new[](size_t, const nothrow_t&) throw();

void operator delete[](void*, const nothrow_t&) throw();

 


(3)從指定空間中分配內(nèi)存
// 分配已有空間給單個對象使用

void* operator new(size_t, void* p) throw() { return p; }

void operator delete(void* p, void*) throw() { } //什么都不做!

// 分配已有空間給對象數(shù)組使用

void* operator new[](size_t, void* p) throw() {return p;}

void operator delete[](void* p, void*) throw() { } //什么也不做!

 


在上述原型中,拋出空異常的函數(shù)都沒有辦法通過拋出std::bad_alloc發(fā)出內(nèi)存耗盡的信號;它們在內(nèi)存分配失敗時返回0。

 

上述原型的使用方法:
class X {

public:

X(){};
X(int n){};

// ...

};
(1)可以拋出異常的new/delete操作符。
原型的第一個參數(shù),即對象(或?qū)ο髷?shù)組)的大小,因此在使用時如下所示:
X* p = new X;

X* p1 = new X(5);

X* pa = new X[10];

分配對象數(shù)組時要注意:只能用這種形式,不能用帶參數(shù)的形式,例如下面的方式是錯誤的:
X* pa2 = new[20] X(5);

你想分配一個X數(shù)組,每個數(shù)組元素都用5進(jìn)行初始化,這是不能做到的。

 

(2)不拋出異常而返回0的new/delete操作符
原型的第二個參數(shù)要求一個nothrow_t的引用,因此必須以<new>中定義的nothrow全局對象作為new/delete的參數(shù),如下所示:

void f()

{
int* p = new int[10000]; // 可能會拋出bad_alloc異常

 


if(int* q = new(nothrow) int[100000]; {

// 內(nèi)存分配成功

delete(nothrow)[]q;
}
else {

// 內(nèi)存分配失敗

}
}

 

6. new與異常
如果在使用new構(gòu)造對象時,構(gòu)造函數(shù)拋出了異常,結(jié)果會怎樣?由new分配的內(nèi)存釋放了嗎?在通常情況下答案是肯定的;但如果是在指定位置上分配對象空間,那么答案就不是這么簡單了。如果這個內(nèi)存塊是由某個類的new函數(shù)分配的,那么就會調(diào)用其相應(yīng)的delete函數(shù)(如果有的話),否則不會有釋放內(nèi)存的動作發(fā)生。這種策略很好地處理了標(biāo)準(zhǔn)庫中的使用指定內(nèi)存的new操作符,以及提供了成對的分配與釋放函數(shù)的任何情形。
看下面這個例子:
void f(Arena& a, X* buffer)

{
X* p1 = new X;

X* p2 = new X[10];

X* p3 = new(buffer[10]) X;

X* p4 = new(buffer[11]) X[10];

X* p5 = new(a) X;

X* p6 = new(a) X[10];

}
分析:p1和p2將能正確釋放其分配的內(nèi)存,不會造成內(nèi)存泄漏,這屬于一種正常情況。后面的四種情況則比較復(fù)雜。對象a如果是采用普通方式分配的內(nèi)存,那么將能夠正確釋放其擁有的內(nèi)存。

 

7. malloc/free沒用了嗎?
new能夠完全替代malloc嗎?絕大部分情況下,答案都是肯定的。但是有一種情況則非用malloc不可了。根據(jù)new的定義,其第一個參數(shù)是待分配對象的大小,但在使用時不需要明確地給出這個值。這個值是由編譯器暗中替你完成的。倘若在某種情況下,需要在分配一個對象的同時還要分配出一些額外的空間用來管理某些相關(guān)的信息。這個額外空間與對象的空間要求連續(xù)。這個時候new就幫不上了。必須用malloc把對象和額外空間的總大小作為malloc的參數(shù)。在分配出來了后,可能需要調(diào)用new的放置形式的調(diào)用在該塊內(nèi)存上構(gòu)造對象。
8. 垃圾收集
當(dāng)我們?yōu)樽约旱念愄峁┝俗约旱膬?nèi)存管理方法時,有可能會出現(xiàn)內(nèi)存分配失敗的情況。因此我們可能會通過set_new_handler()提供一個更靈巧的內(nèi)存釋放與重用機(jī)制。這就為實現(xiàn)垃圾收集提供了一個實現(xiàn)思路。垃圾收集機(jī)制的基本思想是,當(dāng)一個對象不再被引用時,它的內(nèi)存就可以安全地被新的對象所使用。
當(dāng)比較垃圾收集機(jī)制與手工管理方式的代價時,從一下幾個方面進(jìn)行比較:
運(yùn)行時間,內(nèi)存的使用,可靠性,移植性,編程的費(fèi)用,垃圾收集器的費(fèi)用,性能的預(yù)期。

 

垃圾收集器必須要處理幾個重要的問題:
(1)指針的偽裝
通常若以非指針的形式來存儲一個指針,則把這個指針叫做“偽裝的指針”。看下面這個例子:
void f()

{
int* p = new int;

long i1 = reinterpret_cast<long>(p) & 0xFFFF0000;

long i2 = reinterpret_cast<long>(p) & 0x0000FFFF;

p = 0;

// 這里就不存在指向那個整型數(shù)的指針了!

p = reinterpret_cast<int*>(i1|i2);

// 現(xiàn)在這個整型數(shù)又被引用了!

}
上例中原本由p持有的指針被偽裝成兩個整型數(shù)i1和i2。垃圾收集器必須關(guān)注這種偽裝的指針。
指針偽裝還有另外一種形式,即同時有指針和非指針成員的union結(jié)構(gòu),也會給垃圾收集器帶來特殊的問題。看下面的例子:
union U {

int* p;

int i;

};

 

void f(U u, U u2, U u3)

{
u.p = new int;

u2.i = 99999;

u.i = 8;

// ...

}
通常這是不可能知道union中包含的是指針還是整數(shù)。

 

(2)delete函數(shù)
通常若使用了自動垃圾收集,那么delete和delete[]函數(shù)是不再需要了的。但是delete和delete[]函數(shù)除了釋放內(nèi)存的功能外,還會調(diào)用析構(gòu)函數(shù)。因此在這種情況下,下列調(diào)用
delete p;

就只調(diào)用析構(gòu)函數(shù),而內(nèi)存的復(fù)用則向后推遲,直到內(nèi)存塊被收集。一次回收多個對象,有助于減少碎片。

 

(3)析構(gòu)函數(shù)
當(dāng)垃圾收集器準(zhǔn)備回收對象時,有兩種辦法可選:
[1] 為這個對象調(diào)用析構(gòu)函數(shù)(如果有的話);

[2] 將這個對象當(dāng)作原始內(nèi)存(即不調(diào)用析構(gòu)函數(shù))。

一般垃圾收集器會選擇第二中方法。這種方法gc就成為模擬一種無限內(nèi)存的機(jī)制。
也有可能設(shè)計一種gc,它能調(diào)用向它注冊了的對象的析構(gòu)函數(shù)。這種設(shè)計的一個重要方面是防止析構(gòu)函數(shù)重復(fù)刪除一個之前已經(jīng)刪除的對象。

 

(4)內(nèi)存的碎片
處理內(nèi)存碎片的問題上,有兩種主要的GC類型:拷貝型和保守型。拷貝型GC通過移動對象使得碎片空間緊湊;而保守型則通過分配方式的改善來減少碎片。C++的觀點看來,更傾向于保守型的。因為移動對象將導(dǎo)致大量的指針和引用等失效,所以拷貝型GC在C++中幾乎是不可能實現(xiàn)的。此外保守型GC也可以讓C++的代碼段與C代碼段共存。
 

posted on 2008-03-21 09:29 星羅棋布 閱讀(988) 評論(1)  編輯 收藏 引用

評論

# re: new/delete 操作符 2008-03-21 13:43 Colin

不錯
  回復(fù)  更多評論   


只有注冊用戶登錄后才能發(fā)表評論。
網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


導(dǎo)航

統(tǒng)計

常用鏈接

留言簿(1)

隨筆檔案

友情鏈接

搜索

最新評論

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            精品白丝av| 欧美成ee人免费视频| 久久综合九色九九| 免费国产自线拍一欧美视频| 毛片一区二区三区| 最新亚洲电影| 国产精品99久久久久久宅男| 午夜国产不卡在线观看视频| 久久精视频免费在线久久完整在线看| 久久一二三四| 欧美日韩国产限制| 国产主播精品| 亚洲毛片网站| 欧美影视一区| 亚洲精品1区2区| 午夜在线视频观看日韩17c| 久久精品国产免费观看| 欧美二区在线播放| 国产欧美91| 亚洲卡通欧美制服中文| 亚洲综合精品| 欧美成人四级电影| 一区二区三区国产| 久久久久国产精品人| 欧美日韩久久不卡| 伊人精品在线| 午夜精品国产更新| 亚洲精品1区| 久久久久久色| 国产精品人成在线观看免费 | 欧美成人高清视频| 欧美视频日韩视频| 亚洲高清资源综合久久精品| 亚洲一区二区三区午夜| 欧美激情aaaa| 久久国内精品视频| 欧美午夜精品电影| 亚洲乱亚洲高清| 久久综合国产精品台湾中文娱乐网| 亚洲精选久久| 噜噜噜在线观看免费视频日韩 | 美日韩精品免费| 国产偷国产偷亚洲高清97cao| 99精品99久久久久久宅男| 狼人天天伊人久久| 欧美主播一区二区三区美女 久久精品人| 欧美精品系列| 亚洲另类春色国产| 亚洲福利视频一区二区| 久久久99爱| 伊人色综合久久天天| 久久久久久久999| 亚洲一区欧美激情| 国产精品欧美日韩一区二区| 亚洲亚洲精品三区日韩精品在线视频| 亚洲电影下载| 欧美国产精品劲爆| 亚洲七七久久综合桃花剧情介绍| 久久只精品国产| 久久久国产精品一区二区中文| 国产日韩欧美二区| 欧美亚洲一级片| 亚洲综合国产| 国产视频在线观看一区| 欧美在线视频二区| 欧美在线网址| 亚洲第一在线视频| 欧美韩国日本一区| 欧美成人免费网| 999亚洲国产精| 日韩亚洲视频在线| 国产精品美女久久久久aⅴ国产馆| 国产精品99久久久久久久久| 日韩一级在线| 国产欧美日韩专区发布| 久久综合网hezyo| 女女同性精品视频| 一区二区国产在线观看| 国产精品99久久久久久久女警 | 亚洲免费小视频| 国产视频综合在线| 免费看的黄色欧美网站| 国产精品久久国产精麻豆99网站| 国产精品久久久久久影院8一贰佰 国产精品久久久久久影视 | 欧美在线视频在线播放完整版免费观看| 午夜免费久久久久| 亚洲国产欧美一区二区三区久久 | 欧美国产综合| 国产精品xxx在线观看www| 久久九九免费| 欧美日韩1区2区| 久久久夜夜夜| 欧美视频在线不卡| 欧美~级网站不卡| 欧美午夜视频网站| 欧美高清你懂得| 国产精品每日更新| 欧美激情按摩| 国产亚洲欧美一区二区三区| 亚洲精品国产拍免费91在线| 国产一区二区三区四区五区美女| 亚洲欧洲日韩综合二区| 国产在线精品自拍| 亚洲天堂偷拍| 99re亚洲国产精品| 久久三级福利| 欧美亚洲一区在线| 欧美日韩国产小视频| 久久在线视频在线| 国产伦精品一区二区| 亚洲理论在线| 亚洲国产一区二区三区高清| 午夜国产精品影院在线观看 | 性欧美1819sex性高清| 欧美精品国产精品| 欧美不卡高清| 激情五月综合色婷婷一区二区| 在线一区欧美| 一区二区欧美日韩| 欧美激情亚洲激情| 亚洲国产精品第一区二区三区| 一色屋精品视频在线看| 久久激情视频| 久久久精品国产一区二区三区| 国产精品日韩一区二区三区| 一本色道88久久加勒比精品| 9l国产精品久久久久麻豆| 欧美电影免费观看高清| 欧美不卡高清| 亚洲国产视频a| 亚洲精品欧美极品| 欧美高清视频免费观看| 亚洲第一区色| 亚洲精品一区二区三区四区高清| 久久综合色影院| 欧美电影专区| 日韩一级黄色av| 欧美日韩在线免费| 一区二区高清在线| 久久综合导航| 欧美影院在线播放| 亚洲欧美中文另类| 国产精品久久久久久五月尺 | 久久久精品2019中文字幕神马| 久久亚洲春色中文字幕久久久| 国外精品视频| 噜噜噜久久亚洲精品国产品小说| 欧美国产日韩精品| 99re这里只有精品6| 欧美深夜福利| 午夜老司机精品| 美腿丝袜亚洲色图| 亚洲三级电影全部在线观看高清| 欧美激情一区| 亚洲一区二区欧美日韩| 久久精品国产亚洲一区二区| 国产在线播放一区二区三区| 老司机一区二区| 99re成人精品视频| 久久精品30| 亚洲大胆人体视频| 欧美日韩国产综合视频在线观看中文| 一区二区国产在线观看| 欧美在线视频二区| 亚洲欧洲一区二区在线观看| 欧美午夜视频在线观看| 久久精品国产精品亚洲综合| 亚洲人成亚洲人成在线观看| 欧美中文字幕在线播放| 亚洲国产你懂的| 国产精品日本一区二区| 久久伊人一区二区| 日韩视频在线一区二区三区| 久久精品亚洲乱码伦伦中文| 亚洲理论在线| 国产一区视频在线观看免费| 欧美极品欧美精品欧美视频| 性做久久久久久久免费看| 亚洲电影在线播放| 欧美在线黄色| 亚洲精品久久久久久久久| 国产精品亚洲一区| 欧美日韩视频不卡| 久热成人在线视频| 亚洲欧美成人网| 亚洲久久在线| 欧美va天堂在线| 久久精品国产欧美亚洲人人爽| 一区二区三区精品视频在线观看| 国产在线日韩| 国产精品青草久久久久福利99| 模特精品在线| 欧美在线视频二区| 亚洲午夜激情在线| 亚洲激情网址| 一区在线免费观看| 国产亚洲va综合人人澡精品| 欧美亚洲第一页| 欧美日韩日韩| 欧美美女bb生活片|