国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

More Effective C++:不同new和delete

2019-11-17 05:38:37
字體:
供稿:網(wǎng)友

  人們有時似乎喜歡故意使C++語言的術(shù)語難以理解。比如說new操作符(new Operator)和operator new的區(qū)別。

  當(dāng)你寫這樣的代碼:

string *ps = new string("Memory Management");
  你使用的new是new操作符。這個操作符就象sizeof一樣是語言內(nèi)置的,你不能改變它的含義,它的功能總是一樣的。它要完成的功能分成兩部分。第一部分是分配足夠的內(nèi)存以便容納所需類型的對象。第二部分是它調(diào)用構(gòu)造函數(shù)初始化內(nèi)存中的對象。new操作符總是做這兩件事情,你不能以任何方式改變它的行為。

  你所能改變的是如何為對象分配內(nèi)存。new操作符調(diào)用一個函數(shù)來完成必需的內(nèi)存分配,你能夠重寫或重載這個函數(shù)來改變它的行為。new操作符為分配內(nèi)存所調(diào)用函數(shù)的名字是operator new。

  函數(shù)operator new 通常這樣聲明:

void * operator new(size_t size);
  返回值類型是void*,因為這個函數(shù)返回一個未經(jīng)處理(raw)的指針,未初始化的內(nèi)存。(假如你喜歡,你能寫一種operator new函數(shù),在返回一個指針之前能夠初始化內(nèi)存以存儲一些數(shù)值,但是一般不這么做。)參數(shù)size_t確定分配多少內(nèi)存。你能增加額外的參數(shù)重載函數(shù)operator new,但是第一個參數(shù)類型必須是size_t。(有關(guān)operator new更多的信息參見Effective C++ 條款8至條款10。)

  你一般不會直接調(diào)用operator new,但是一旦這么做,你可以象調(diào)用其它函數(shù)一樣調(diào)用它:

void *rawMemory = operator new(sizeof(string));
  操作符operator new將返回一個指針,指向一塊足夠容納一個string類型對象的內(nèi)存。

  就象malloc一樣,operator new的職責(zé)只是分配內(nèi)存。它對構(gòu)造函數(shù)一無所知。operator new所了解的是內(nèi)存分配。把operator new 返回的未經(jīng)處理的指針傳遞給一個對象是new操作符的工作。當(dāng)你的編譯器遇見這樣的語句:

string *ps = new string("Memory Management");
  它生成的代碼或多或少與下面的代碼相似(更多的細(xì)節(jié)見Effective C++條款8和條款10,還有我的文章Counting object里的注釋。):

void *memory = // 得到未經(jīng)處理的內(nèi)存
operator new(sizeof(string)); // 為String對象
call string::string("Memory Management") //初始化
on *memory; // 內(nèi)存中

// 的對象

string *ps = // 是ps指針指向
static_cast<string*>(memory); // 新的對象
  注重第二步包含了構(gòu)造函數(shù)的調(diào)用,你做為一個程序員被禁止這樣去做。你的編譯器則沒有這個約束,它可以做它想做的一切。因此假如你想建立一個堆對象就必須用new操作符,不能直接調(diào)用構(gòu)造函數(shù)來初始化對象。

  Placement new

  有時你確實(shí)想直接調(diào)用構(gòu)造函數(shù)。在一個已存在的對象上調(diào)用構(gòu)造函數(shù)是沒有意義的,因為構(gòu)造函數(shù)用來初始化對象,而一個對象僅僅能在給它初值時被初始化一次。但是有時你有一些已經(jīng)被分配但是尚未處理的的(raw)內(nèi)存,你需要在這些內(nèi)存中構(gòu)造一個對象。你可以使用一個非凡的operator new ,它被稱為placement new。

  下面的例子是placement new如何使用,考慮一下:

class Widget {
 public:
  Widget(int widgetSize);
  ...
};

Widget * constrUCtWidgetInBuffer(void *buffer,

int widgetSize)
{
 return new (buffer) Widget(widgetSize);
}
  這個函數(shù)返回一個指針,指向一個Widget對象,對象在轉(zhuǎn)遞給函數(shù)的buffer里分配。當(dāng)程序使用共享內(nèi)存或memory-mapped I/O時這個函數(shù)可能有用,因為在這樣程序里對象必須被放置在一個確定地址上或一塊被例程分配的內(nèi)存里。(參見條款4,一個如何使用placement new的一個不同例子。)

  在constructWidgetInBuffer里面,返回的表達(dá)式是:

new (buffer) Widget(widgetSize)

  這初看上去有些生疏,但是它是new操作符的一個用法,需要使用一個額外的變量(buffer),當(dāng)new操作符隱含調(diào)用operator new函數(shù)時,把這個變量傳遞給它。被調(diào)用的operator new函數(shù)除了待有強(qiáng)制的參數(shù)size_t外,還必須接受void*指針參數(shù),指向構(gòu)造對象占用的內(nèi)存空間。這個operator new就是placement new,它看上去象這樣:

void * operator new(size_t, void *location)
{
 return location;
}
  這可能比你期望的要簡單,但是這就是placement new需要做的事情。究竟operator new的目的是為對象分配內(nèi)存然后返回指向該內(nèi)存的指針。在使用placement new的情況下,調(diào)用者已經(jīng)獲得了指向內(nèi)存的指針,因為調(diào)用者知道對象應(yīng)該放在哪里。placement new必須做的就是返回轉(zhuǎn)遞給它的指針。(沒有用的(但是強(qiáng)制的)參數(shù)size_t沒有名字,以防止編譯器發(fā)出警告說它沒有被使用;見條款6。) placement new是標(biāo)準(zhǔn)C++庫的一部分。為了使用placement new,你必須使用語句#include <new>(或者假如你的編譯器還不支持這新風(fēng)格的頭文件名)。

  讓我們從placement new回來片刻,看看new操作符(new operator)與operator new的關(guān)系,你想在堆上建立一個對象,應(yīng)該用new操作符。它既分配內(nèi)存又為對象調(diào)用構(gòu)造函數(shù)。假如你僅僅想分配內(nèi)存,就應(yīng)該調(diào)用operator new函數(shù);它不會調(diào)用構(gòu)造函數(shù)。假如你想定制自己的在堆對象被建立時的內(nèi)存分配過程,你應(yīng)該寫你自己的operator new函數(shù),然后使用new操作符,new操作符會調(diào)用你定制的operator new。假如你想在一塊已經(jīng)獲得指針的內(nèi)存里建立一個對象,應(yīng)該用placement new。

  Deletion and Memory Deallocation

  為了避免內(nèi)存泄漏,每個動態(tài)內(nèi)存分配必須與一個等同相反的deallocation對應(yīng)。函數(shù)operator delete與delete操作符的關(guān)系與operator new與new操作符的關(guān)系一樣。當(dāng)你看到這些代碼:


string *ps;
...
delete ps; // 使用delete 操作符
  你的編譯器會生成代碼來析構(gòu)對象并釋放對象占有的內(nèi)存。

  Operator delete用來釋放內(nèi)存,它被這樣聲明:

void operator delete(void *memoryToBeDeallocated);
  因此, delete ps;

  導(dǎo)致編譯器生成類似于這樣的代碼:

ps->~string(); // call the object's dtor
operator delete(ps); // deallocate the memory

// the object occupied
  這有一個隱含的意思是假如你只想處理未被初始化的內(nèi)存,你應(yīng)該繞過new和delete操作符,而調(diào)用operator new 獲得內(nèi)存和operator delete釋放內(nèi)存給系統(tǒng):

void *buffer = // 分配足夠的
operator new(50*sizeof(char)); // 內(nèi)存以容納50個char

//沒有調(diào)用構(gòu)造函數(shù)

...
operator delete(buffer); // 釋放內(nèi)存

// 沒有調(diào)用析構(gòu)函數(shù)
  這與在C中調(diào)用malloc和free等同。

  假如你用placement new在內(nèi)存中建立對象,你應(yīng)該避免在該內(nèi)存中用delete操作符。因為delete操作符調(diào)用operator delete來釋放內(nèi)存,但是包含對象的內(nèi)存最初不是被operator new分配的,placement new只是返回轉(zhuǎn)遞給它的指針。誰知道這個指針來自何方?而你應(yīng)該顯式調(diào)用對象的析構(gòu)函數(shù)來解除構(gòu)造函數(shù)的影響:

// 在共享內(nèi)存中分配和釋放內(nèi)存的函數(shù) void * mallocShared(size_t size);

void freeShared(void *memory);
void *sharedMemory = mallocShared(sizeof(Widget));
Widget *pw = // 如上所示,
constructWidgetInBuffer(sharedMemory, 10); // 使用

// placement new

...
delete pw; // 結(jié)果不確定! 共享內(nèi)存來自
// mallocShared, 而不是operator new

pw->~Widget(); // 正確。 析構(gòu) pw指向的Widget,

// 但是沒有釋放
//包含Widget的內(nèi)存

freeShared(pw); // 正確。 釋放pw指向的共享內(nèi)存

// 但是沒有調(diào)用析構(gòu)函數(shù)
  如上例所示,假如傳遞給placement new的raw內(nèi)存是自己動態(tài)分配的(通過一些不常用的方法),假如你希望避免內(nèi)存泄漏,你必須釋放它。(參見我的文章Counting objects里面關(guān)于placement delete的注釋。)

  Arrays

  到目前為止一切順利,但是還得接著走。到目前為止我們所測試的都是一次建立一個對象。怎樣分配數(shù)組?會發(fā)生什么?

string *ps = new string[10]; // allocate an array of
// objects
  被使用的new仍然是new操作符,但是建立數(shù)組時new操作符的行為與單個對象建立有少許不同。第一是內(nèi)存不再用operator new分配,代替以等同的數(shù)組分配函數(shù),叫做operator new[](經(jīng)常被稱為array new)。它與operator new一樣能被重載。這就答應(yīng)你控制數(shù)組的內(nèi)存分配,就象你能控制單個對象內(nèi)存分配一樣(但是有一些限制性說明,參見Effective C++ 條款8)。

  (operator new[]對于C++來說是一個比較新的東西,所以你的編譯器可能不支持它。假如它不支持,無論在數(shù)組中的對象類型是什么,全局operator new將被用來給每個數(shù)組分配內(nèi)存。在這樣的編譯器下定制數(shù)組內(nèi)存分配是困難的,因為它需要重寫全局operator new。這可不是一個能輕易接受的任務(wù)。缺省情況下,全局operator new處理程序中所有的動態(tài)內(nèi)存分配,所以它行為的任何改變都將有深入和普遍的影響。而且全局operator new有一個正常的簽名(normal signature)(也就單一的參數(shù)size_t,參見Effective C++條款9),所以假如你 決定用自己的方法聲明它,你馬上使你的程序與其它庫不兼容基于這些考慮,在缺乏operator new[]支持的編譯器里為數(shù)組定制內(nèi)存治理不是一個合理的設(shè)計。)

  第二個不同是new操作符調(diào)用構(gòu)造函數(shù)的數(shù)量。對于數(shù)組,在數(shù)組里的每一個對象的構(gòu)造函數(shù)都必須被調(diào)用:

string *ps = // 調(diào)用operator new[]為10個
new string[10]; // string對象分配內(nèi)存,

// 然后對每個數(shù)組元素調(diào)用
// string對象的缺省構(gòu)造函數(shù)。
  同樣當(dāng)delete操作符用于數(shù)組時,它為每個數(shù)組元素調(diào)用析構(gòu)函數(shù),然后調(diào)用operator delete來釋放內(nèi)存。

  就象你能替換或重載operator delete一樣,你也替換或重載operator delete[]。在它們重載的方法上有一些限制。請參考優(yōu)秀的C++教材。

  new和delete操作符是內(nèi)置的,其行為不受你的控制,凡是它們調(diào)用的內(nèi)存分配和釋放函數(shù)則可以控制。當(dāng)你想定制new和delete操作符的行為時,請記住你不能真的做到這一點(diǎn)。你只能改變它們?yōu)橥瓿伤鼈兊墓δ芩扇〉姆椒ǎ鼈兯瓿傻墓δ軇t被語言固定下來,不能改變。(You can modify how they do what they do, but what they do is fixed by the language)

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 吉林省| 松阳县| 新巴尔虎左旗| 罗源县| 盐城市| 清水河县| 玉田县| 雅安市| 巴南区| 棋牌| 温州市| 陆川县| 天水市| 普兰店市| 治多县| 巨野县| 芦溪县| 霍林郭勒市| 建德市| 道孚县| 武川县| 兰州市| 青浦区| 大竹县| 辽中县| 富顺县| 怀仁县| 盐津县| 吉安县| 积石山| 亚东县| 尼勒克县| 泰州市| 辽宁省| 辰溪县| 阿荣旗| 龙海市| 武义县| 钟祥市| 大渡口区| 神木县|