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

海納百川

 

2008年3月21日

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操作符分配的對象都應該用delete操作符手動地銷毀掉。C++標準并沒有定義任何形式的“垃圾收集”機制。delete操作符只能用于由new返回的指針,或者是零。當delete的參數(shù)是0時,不會產(chǎn)生任何效果,也就是說這個delete操作符對應的函數(shù)根本就不會被執(zhí)行。

 

new(delete)既可以分配(釋放)單個的對象(當然也包括內(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++標準中并沒有要求new操作符對分配出來的空間進行初始化。下面是使用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的缺省構造函數(shù)對空間進行初始化。注意,上述兩種形式都是可以的,無論X是內(nèi)建類型還是自定義用戶類型。對于類來說,還可以使用類的構造函數(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指針,也不能修改對象(很好理解,當調(diào)用new的時候?qū)ο筮€沒有真正創(chuà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é)將要詳述),也可能還有其他的更好的更適合你的應用的方式。

 

那么如何重載數(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)相關的,因此對于用戶程序員來說是不必要知道的。故而這里只提供單參數(shù)版本就可以了。(這倒是提供了一種查看這個delta量的方法。根據(jù)實際測試,GCC 4.1采用了4個字節(jié)的delta量。)

 


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

class Manager : public Employee {

int level;

// ...

};
void f()

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

delete p;

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

 

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

public:
X(int);
//...
};
當需要把對象放置到指定地方的時候,只需要為分配函數(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ā)生什么呢?默認情況下,這個分配器會拋出一個bad_alloc異常對象。看下面的例子:
void f()

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

}
catch(bad_alloc) {

cerr << "Memory exhausted!\n";

}
}
[疑問:構造一個異常對象也需要內(nèi)存空間,既然已經(jīng)內(nèi)存耗盡了,那這個內(nèi)存又從哪里來呢?]
可以自定義內(nèi)存耗盡時的處理方法(new_handler)。當new操作失敗時,首先會調(diào)用一個由set_new_handler()指定的函數(shù)。我們可以自定義這個函數(shù),然后用set_new_handler()來登記。最后當new操作失敗時可以調(diào)用適當?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";

 


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

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


5. 標準頭文件<new>中的原型
下面是標準頭文件中的各種原型聲明:
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進行初始化,這是不能做到的。

 

(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構造對象時,構造函數(shù)拋出了異常,結(jié)果會怎樣?由new分配的內(nèi)存釋放了嗎?在通常情況下答案是肯定的;但如果是在指定位置上分配對象空間,那么答案就不是這么簡單了。如果這個內(nèi)存塊是由某個類的new函數(shù)分配的,那么就會調(diào)用其相應的delete函數(shù)(如果有的話),否則不會有釋放內(nèi)存的動作發(fā)生。這種策略很好地處理了標準庫中的使用指定內(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)存泄漏,這屬于一種正常情況。后面的四種情況則比較復雜。對象a如果是采用普通方式分配的內(nèi)存,那么將能夠正確釋放其擁有的內(nèi)存。

 

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

 

垃圾收集器必須要處理幾個重要的問題:
(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。垃圾收集器必須關注這種偽裝的指針。
指針偽裝還有另外一種形式,即同時有指針和非指針成員的union結(jié)構,也會給垃圾收集器帶來特殊的問題。看下面的例子:
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)用析構函數(shù)。因此在這種情況下,下列調(diào)用
delete p;

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

 

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

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

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

 

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

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

僅列出標題  

導航

統(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免费看影院| 欧美日韩不卡视频| 欧美精品一区二区高清在线观看| 浪潮色综合久久天堂| 欧美成人蜜桃| 欧美日韩国产欧美日美国产精品| 嫩草成人www欧美| 欧美激情bt| 国产欧美精品xxxx另类| 国语自产精品视频在线看8查询8| 黄页网站一区| 一区二区不卡在线视频 午夜欧美不卡在 | 久久久久久久97| 久久久久亚洲综合| 久久夜色精品国产亚洲aⅴ | 亚洲人被黑人高潮完整版| 1024亚洲| 亚洲精品欧洲| 亚洲欧美视频一区二区三区| 国产精品制服诱惑| 在线日韩精品视频| 一本久久综合| 久久久久国产成人精品亚洲午夜| 亚洲午夜三级在线| 老司机成人在线视频| 欧美精品97| 一区在线影院| 99精品国产在热久久婷婷| 欧美伊人久久久久久午夜久久久久 | 欧美精品在线播放| 国产精品自在欧美一区| 亚洲国产日韩欧美| 午夜精品理论片| 欧美国产第二页| 欧美在线一级视频| 国产亚洲午夜| 国产欧美在线观看| 亚洲美女在线观看| 久久人人爽人人| 日韩视频在线一区二区| 欧美在线视频播放| 欧美色中文字幕| 最新高清无码专区| 久久资源在线| 欧美一区二区三区在线观看| 欧美日韩国产在线播放| 永久久久久久| 久久久夜夜夜| 亚洲伊人第一页| 欧美视频一二三区| 亚洲麻豆国产自偷在线| 免费日韩视频| 久久久久国产一区二区三区| 国产嫩草一区二区三区在线观看| 亚洲视频在线观看| 亚洲精品一二区| 欧美精品 日韩| 日韩亚洲欧美高清| 91久久在线| 久久久免费观看视频| 激情亚洲网站| 久久久久久久精| 午夜亚洲福利| 精品1区2区3区4区| 久久综合九色综合久99| 欧美一级黄色网| 国产曰批免费观看久久久| 久久激情一区| 久久精品青青大伊人av| 激情文学一区| 亚洲国产精品一区二区久| 欧美激情四色| 在线亚洲欧美| 亚洲专区国产精品| 国产一区二区视频在线观看| 久久久久国产精品人| 久久久综合精品| 日韩视频免费看| 亚洲小视频在线| 国内综合精品午夜久久资源| 免费看的黄色欧美网站| 欧美1区2区视频| 亚洲一级片在线观看| 亚洲视频一区二区| 国一区二区在线观看| 亚洲国产欧洲综合997久久| 欧美深夜影院| 麻豆成人小视频| 欧美日韩二区三区| 久久久天天操| 欧美激情中文字幕在线| 欧美亚洲视频在线看网址| 久久久久久久久久久一区 | 一本色道久久综合狠狠躁篇怎么玩 | 久久久久久久一区二区三区| 在线色欧美三级视频| 亚洲欧洲一区二区天堂久久| 国产精品国产成人国产三级| 另类av一区二区| 欧美视频在线视频| 欧美aⅴ一区二区三区视频| 欧美日韩激情网| 久久综合一区二区| 国产精品国产a| 欧美黄色网络| 激情久久婷婷| 亚洲综合首页| 日韩午夜免费| 99re6热在线精品视频播放速度| 国产日韩专区在线| 日韩视频一区二区三区在线播放免费观看 | 久久精品国产第一区二区三区| 揄拍成人国产精品视频| 一本大道久久精品懂色aⅴ| 国产精品视频一二三| 亚洲国产aⅴ天堂久久| 国产日韩精品视频一区二区三区| 亚洲激情第一区| 黄色在线成人| 亚洲一区日韩| 亚洲一二三四久久| 欧美日本免费| 欧美黄色网络| 在线成人av| 欧美一区二区三区视频在线| 一区二区三区回区在观看免费视频| 久久精品国产一区二区电影| 性色av一区二区三区| 欧美日韩综合在线| 亚洲精品免费在线观看| 亚洲精品久久久久久久久久久| 久久精品一区四区| 久久久精品日韩| 国产亚洲精品美女| 午夜精品免费在线| 久久国产一区二区| 国产自产女人91一区在线观看| 午夜在线视频一区二区区别 | 艳女tv在线观看国产一区| av不卡在线| 欧美午夜精品理论片a级大开眼界| 亚洲每日更新| 亚洲性夜色噜噜噜7777| 欧美系列电影免费观看| 亚洲欧美日本国产专区一区| 欧美一区二区三区视频免费| 国产精品一区二区在线观看| 久久狠狠婷婷| 亚洲电影免费在线| 亚洲深夜福利在线| 中文av字幕一区| 欧美日韩国语| 亚洲永久视频| 噜噜噜在线观看免费视频日韩| 亚洲国产91| 欧美日韩中国免费专区在线看| 亚洲一二三区在线| 美女国内精品自产拍在线播放| 最新亚洲视频| 国产精品久久久久久久久久久久| 欧美一区二区在线看| 亚洲国产精品精华液2区45| 亚洲婷婷综合久久一本伊一区| 国产日产亚洲精品| 欧美精品色网| 欧美一区二区精品| 亚洲电影免费观看高清| 午夜精品久久久久久久久久久久久| 国精品一区二区三区| 欧美国产日韩一二三区| 亚洲一区日韩| 亚洲精品欧美精品| 久久精品在线视频| 99在线观看免费视频精品观看| 国产精品永久入口久久久| 美女黄网久久| 亚洲一区二区三区视频| 免费欧美电影| 亚洲欧美视频| 亚洲美女免费视频| 国产精品欧美日韩一区| 欧美激情综合色综合啪啪| 欧美一区亚洲| 亚洲欧洲日韩女同| 欧美激情一区在线观看| 国内精品久久久久久影视8| 欧美性理论片在线观看片免费| 久久人人爽人人爽爽久久| 亚洲国产综合在线| 久久综合伊人77777蜜臀| 亚洲一区二区视频| 亚洲免费高清视频| 在线日本成人| 国产精品欧美日韩一区| 国产精品jvid在线观看蜜臀| 久久国产婷婷国产香蕉| 亚洲欧美激情视频| 久久人人看视频| 久久在线91|