C++ 網(wǎng)易面試題“讓new操作符不分配內(nèi)存诞外,只調(diào)用構(gòu)造函數(shù)”

問題

c++中的new操作符 通常完成兩個(gè)工作 分配內(nèi)存及調(diào)用相應(yīng)的構(gòu)造函數(shù)。
請(qǐng)問:
1)如何讓new操作符不分配內(nèi)存灾票,只調(diào)用構(gòu)造函數(shù)峡谊?
2)這樣的用法有什么用?

placement new的含義

placement new可以實(shí)現(xiàn)不分配內(nèi)存刊苍,只調(diào)用構(gòu)造函數(shù)既们。

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

placement new的執(zhí)行忽略了size_t參數(shù),只返還第二個(gè)參數(shù)正什。
其結(jié)果是允許用戶把一個(gè)對(duì)象放到一個(gè)特定的地方啥纸,達(dá)到調(diào)用構(gòu)造函數(shù)的效果。

用法如下:

#include <iostream>
#include <new>

class Test
{
public:
    Test()
    {
        std::cout << "Constructor" << std::endl;
    };
    ~Test()
    {
        std::cout << "Destructor" << std::endl;
    }
private:
    char mA;
    char mB;
};

char* gMemoryCache = (char *)malloc(sizeof(Test));

int main()
{
    {
        Test* test = new(gMemoryCache) Test();
    }
    {
        Test* test = new(gMemoryCache) Test();
        test->~Test();
    }
}

輸出:

Constructor
Constructor
Destructor

和其他普通的new不同的是婴氮,它在括號(hào)里多了另外一個(gè)參數(shù)斯棒。比如:

Widget * p = new Widget; - - - - - - - - - //ordinary new 
pi = new (ptr) int; pi = new (ptr) int;     //placement new

括號(hào)里的參數(shù)ptr是一個(gè)指針,它指向一個(gè)內(nèi)存緩沖器主经,placement new將在這個(gè)緩沖器上分配一個(gè)對(duì)象名船。
Placement new的返回值是這 個(gè)被構(gòu)造對(duì)象的地址(比如括號(hào)中的傳遞參數(shù))。

placement new主要適用于:在對(duì)時(shí)間要求非常高的應(yīng)用程序中旨怠,因?yàn)檫@些程序分配的時(shí)間是確定 的渠驼;長(zhǎng)時(shí)間運(yùn)行而不被打斷的程序;以及執(zhí)行一個(gè)垃圾收集器 (garbage collector)鉴腻。

new 迷扇、operator new 和 placement new 區(qū)別

  • new :不能被重載,其行為總是一致的爽哎。它先調(diào)用operator new分配內(nèi)存蜓席,然后調(diào)用構(gòu)造函數(shù)初始化那段內(nèi)存。
  • operator new:要實(shí)現(xiàn)不同的內(nèi)存分配行為课锌,應(yīng)該重載operator new厨内,而不是new祈秕。
  • delete和operator delete類似。 delete首先調(diào)用對(duì)象的析構(gòu)函數(shù)雏胃,然后調(diào)用operator delete釋放掉所使用的內(nèi)存请毛。
  • placement new:只是operator new重載的一個(gè)版本。它并不分配內(nèi)存瞭亮,只是返回指向已經(jīng)分配好的某段內(nèi)存的一個(gè)指針方仿。因此不能刪除它,但需要調(diào)用對(duì)象的析構(gòu)函數(shù)统翩。

new 操作符的執(zhí)行過程
  (1). 調(diào)用operator new分配內(nèi)存 仙蚜;
  (2). 調(diào)用構(gòu)造函數(shù)生成類對(duì)象;
  (3). 返回相應(yīng)指針厂汗。

placement new允許你在一個(gè)已經(jīng)分配好的內(nèi)存中(椢郏或者堆中)構(gòu)造一個(gè)新的對(duì)象。原型中void*p實(shí)際上就是指向一個(gè)已經(jīng)分配 好的內(nèi)存緩沖區(qū)的的首地址娶桦。

Placement new 存在的理由

用Placement new 解決buffer的問題

問題描述:用new分配的數(shù)組緩沖時(shí)贾节,由于調(diào)用了默認(rèn)構(gòu)造函數(shù),因此執(zhí)行效率上不佳趟紊。若沒有默認(rèn)構(gòu)造函數(shù)則會(huì)發(fā)生編譯時(shí)錯(cuò)誤。如果你想在預(yù)分配的內(nèi)存上創(chuàng)建對(duì)象碰酝,用缺省的new操作符是行不通的霎匈。要解決這個(gè)問題,你可以用placement new構(gòu)造送爸。它允許你構(gòu)造一個(gè)新對(duì)象到預(yù)分配的內(nèi)存上铛嘱。

增大時(shí)空效率的問題

使用new操作符分配內(nèi)存需要在堆中查找足夠大的剩余空間,顯然這個(gè)操作速度是很慢的袭厂,而且有可能出現(xiàn)無法分配內(nèi)存的異常(空間不夠)墨吓。
placement new 就可以解決這個(gè)問題。我們構(gòu)造對(duì)象都是在一個(gè)預(yù)先準(zhǔn)備好了的內(nèi)存緩沖區(qū)中進(jìn)行纹磺,不需要查找內(nèi)存帖烘,內(nèi)存分配的時(shí)間是常數(shù);而且不會(huì)出現(xiàn)在程序運(yùn)行中途出現(xiàn)內(nèi) 存不足的異常橄杨。所以秘症,placement new非常適合那些對(duì)時(shí)間要求比較高,長(zhǎng)時(shí)間運(yùn)行不希望被打斷的應(yīng)用程序式矫。

使用步驟

在很多情況下乡摹,placement new的使用方法和其他普通的new有所不同。這里提供了它的使用步驟采转。

第一步 緩存提前分配

有三種方式:
1.為了保證通過placement new使用的緩存區(qū)的memory alignmen(內(nèi)存隊(duì)列)正確準(zhǔn)備聪廉,使用普通的new來分配它:在堆上進(jìn)行分配

class Task ;
char * buff = new [sizeof(Task)]; //分配內(nèi)存

(請(qǐng)注意auto或者static內(nèi)存并非都正確地為每一個(gè)對(duì)象類型排列,所以,你將不能以placement new使用它們板熊。)

2.在棧上進(jìn)行分配

class Task ;
char buf[N*sizeof(Task)]; //分配內(nèi)存

3.還有一種方式框全,就是直接通過地址來使用。(必須是有意義的地址)

void* buf = reinterpret_cast<void*> (0xF00F);

第二步:對(duì)象的分配

在剛才已分配的緩存區(qū)調(diào)用placement new來構(gòu)造一個(gè)對(duì)象邻邮。

Task *ptask = new (buf) Task

第三步:使用

按照普通方式使用分配的對(duì)象:

ptask->memberfunction();
ptask-> member;
//...

第四步:對(duì)象的析構(gòu)

一旦你使用完這個(gè)對(duì)象竣况,你必須調(diào)用它的析構(gòu)函數(shù)來毀滅它。按照下面的方式調(diào)用析構(gòu)函數(shù):

ptask->~Task(); //調(diào)用外在的析構(gòu)函數(shù)

第五步:釋放

你可以反復(fù)利用緩存并給它分配一個(gè)新的對(duì)象(重復(fù)步驟2筒严,3丹泉,4)如果你不打算再次使用這個(gè)緩存,你可以象這樣釋放它:

delete [] buf;

跳過任何步驟就可能導(dǎo)致運(yùn)行時(shí)間的崩潰鸭蛙,內(nèi)存泄露摹恨,以及其它的意想不到的情況。如果你確實(shí)需要使用placement new娶视,請(qǐng)認(rèn)真遵循以上的步驟晒哄。

性能對(duì)比

采用placement new和new的方式創(chuàng)建和刪除對(duì)象一萬次,統(tǒng)計(jì)時(shí)間肪获,單位是us寝凌。

int main()
{
    {
        uint64_t start = GetCurrentTimeInMicroSeconds();
        for (uint32_t i = 0; i < 10000; ++i)
        {
            Test* test = new(gMemoryCache) Test();
            test->~Test();
        }
        std::cout << GetCurrentTimeInMicroSeconds() - start << std::endl;
    }
    {
        uint64_t start = GetCurrentTimeInMicroSeconds();
        for (uint32_t i = 0; i < 10000; ++i)
        {
            Test* test = new Test();
            delete test;
        }
        std::cout << GetCurrentTimeInMicroSeconds() - start << std::endl;
    }
}

結(jié)果:

placement new: 186
new : 1448

結(jié)論:在頻繁構(gòu)造和析構(gòu)對(duì)象的場(chǎng)景中,placement new對(duì)性能有7倍的提升孝赫。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末较木,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子青柄,更是在濱河造成了極大的恐慌伐债,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,919評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件致开,死亡現(xiàn)場(chǎng)離奇詭異峰锁,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)双戳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門虹蒋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人飒货,你說我怎么就攤上這事千诬。” “怎么了膏斤?”我有些...
    開封第一講書人閱讀 163,316評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵徐绑,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我莫辨,道長(zhǎng)傲茄,這世上最難降的妖魔是什么毅访? 我笑而不...
    開封第一講書人閱讀 58,294評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮盘榨,結(jié)果婚禮上喻粹,老公的妹妹穿的比我還像新娘。我一直安慰自己草巡,他們只是感情好守呜,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,318評(píng)論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著山憨,像睡著了一般查乒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上郁竟,一...
    開封第一講書人閱讀 51,245評(píng)論 1 299
  • 那天玛迄,我揣著相機(jī)與錄音,去河邊找鬼棚亩。 笑死蓖议,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的讥蟆。 我是一名探鬼主播勒虾,決...
    沈念sama閱讀 40,120評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼瘸彤!你這毒婦竟也來了修然?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,964評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤钧栖,失蹤者是張志新(化名)和其女友劉穎低零,沒想到半個(gè)月后婆翔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拯杠,經(jīng)...
    沈念sama閱讀 45,376評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,592評(píng)論 2 333
  • 正文 我和宋清朗相戀三年啃奴,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了潭陪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,764評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡最蕾,死狀恐怖依溯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情瘟则,我是刑警寧澤黎炉,帶...
    沈念sama閱讀 35,460評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站醋拧,受9級(jí)特大地震影響慷嗜,放射性物質(zhì)發(fā)生泄漏淀弹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,070評(píng)論 3 327
  • 文/蒙蒙 一庆械、第九天 我趴在偏房一處隱蔽的房頂上張望薇溃。 院中可真熱鬧,春花似錦缭乘、人聲如沸沐序。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽策幼。三九已至,卻和暖如春逛尚,著一層夾襖步出監(jiān)牢的瞬間垄惧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工绰寞, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留到逊,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,819評(píng)論 2 370
  • 正文 我出身青樓滤钱,卻偏偏與公主長(zhǎng)得像觉壶,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子件缸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,665評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容