Big Three & operator new/delete[GeekBand]

1.Big Three

當(dāng)我們定義一個(gè)類(lèi)以后有,如果沒(méi)實(shí)現(xiàn)這拷貝構(gòu)造函數(shù)、拷貝復(fù)制函數(shù)和析構(gòu)函數(shù),編譯器會(huì)自動(dòng)為我們生成這3個(gè)函數(shù)。但是,編譯器自動(dòng)生成的這拷貝構(gòu)造函數(shù)和拷貝復(fù)制函數(shù)只進(jìn)行簡(jiǎn)單的內(nèi)存復(fù)制。如果我們定義的類(lèi)的數(shù)據(jù)成員包括指針的話,使用編譯器自動(dòng)生成的這套函數(shù)就會(huì)有問(wèn)題(復(fù)制和拷貝的對(duì)象實(shí)際都指向同一個(gè)地方,這并不是我們想要的),因此如果類(lèi)中包含帶指針的數(shù)據(jù)成員則一定要實(shí)現(xiàn)這三個(gè)函數(shù)。

1.1拷貝構(gòu)造函數(shù)(copy ctor)

如果一個(gè)構(gòu)造函數(shù)的第一個(gè)參數(shù)是自身類(lèi)類(lèi)型的引用,且任何額外的參數(shù)都有默認(rèn)值,則此構(gòu)造函數(shù)是拷貝構(gòu)造函數(shù)。拷貝構(gòu)造函數(shù)被調(diào)用的情況包括:

  • a)使用=定義變量,如果A b; A a = b;
  • b)將一個(gè)對(duì)象作為實(shí)參傳遞給非引用類(lèi)型的形參;
  • c)從一個(gè)返回類(lèi)型為非引用的類(lèi)型的函數(shù)返回一個(gè)對(duì)象
  • d)用花括號(hào)初始化一個(gè)數(shù)組的成員或者聚合類(lèi)的成員

1.2拷貝賦值函數(shù)(copy op=)

拷貝賦值函數(shù),其實(shí)就是對(duì)賦值操作符(=)進(jìn)行重載。一般,拷貝賦值函數(shù)接受自身類(lèi)類(lèi)型的引用作為參數(shù)并返回自身類(lèi)類(lèi)型的引用。如:A& operator=(const A& rhs);

1.3析構(gòu)函數(shù)(dtor)

析構(gòu)函數(shù),釋放對(duì)象使用的資源,并銷(xiāo)毀對(duì)象的非static數(shù)據(jù)成員。析構(gòu)函數(shù)被調(diào)用的情況:

  • a)離開(kāi)作用域
  • b)對(duì)象被銷(xiāo)毀
  • c)容器被銷(xiāo)毀時(shí),其元素也被銷(xiāo)毀
  • d)動(dòng)態(tài)分配的對(duì)象,調(diào)用對(duì)應(yīng)的delete
  • e)對(duì)于臨時(shí)對(duì)象, 當(dāng)創(chuàng)建他的表達(dá)式結(jié)束時(shí)。

2.stack & heap

2.1 stack & heap 定義

  • a)stack
    所謂的stack,是存在于某作用域(scope)的一塊內(nèi)存空間。
  • b)heap
    Heap,或謂system heap,是由操作系統(tǒng)提供的一塊global內(nèi)存空間,程序可動(dòng)態(tài)分配從中獲取若干區(qū)塊。

2.2 new & delete

2.2.1 new

c++可以使用new來(lái)分配內(nèi)存空間。new的用法有三種,包括:

  • a)plain new
    plain new其實(shí)就是我們最常使用的方式,new分配內(nèi)存失敗時(shí)并不是返回NULL,而是拋出異常std::bad_alloc.std::bad_alloc和new的聲明在頭文件new中。
    函數(shù)聲明:
void* operator new(std::size_t) _GLIBCXX_THROW (std::bad_alloc)  __attribute__((__externally_visible__));
void* operator new[](std::size_t) _GLIBCXX_THROW (std::bad_alloc)  __attribute__((__externally_visible__));

用法:

#include <new>
std::size_t sz = 100;
char *parr = NULL;
char *p = NULL;
try{
    parr = new char[sz];
    p = new char;
}catch(std::bad_alloc& ex){
    std::cout << ex.what() << std::endl;
}
  • b)nothrow new
    nothrow new和plain new唯一的不同在于,new分配失敗后并不拋出異常,而是返回NULL.同樣nothrow new也在頭文件<new>中聲明。函數(shù)聲明:
void* operator new(std::size_t, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT  __attribute__((__externally_visible__));
void* operator new[](std::size_t, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT  __attribute__((__externally_visible__));

用法:

#include <new>
#include <iostream>
std::size_t sz = 100;
char *parr = new(nothrow) char[sz];
if (!parr){
    std::cout << "new error" << std::endl;
}
char *p = new(nothrow) char;
if (!p){
    std::cout << "new error" << std::endl;
}

說(shuō)明:nothrow是頭文件聲明的對(duì)象,可以直接使用。

  • c)placement new

函數(shù)聲明:

inline void* operator new(std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT;
inline void* operator new[](std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT;

用法:

void *buffer = new char[sz];
String *p = new(buffer) String[2];

按照標(biāo)準(zhǔn)庫(kù)<new>中的分類(lèi),plain new和nothrow new是replaceable,用戶可以通過(guò)重載操作符來(lái)實(shí)現(xiàn)自定義的內(nèi)存管理,placement new是不可重載的操作。

  • plain new和nothrow new執(zhí)行的動(dòng)作如下:
    1.分配空間
    2.初始化對(duì)象
    比如說(shuō),我們執(zhí)行Complex *p = new Complex(1, 2);等價(jià)于如下代碼:
void *mem = operator new(sizeof(Complex));  //分配空間
pc = static_cast<Complex *>men; //類(lèi)型轉(zhuǎn)換
pc->Complex::Complex(1,2); //初始化對(duì)象
  • placement new執(zhí)行的動(dòng)作如下:
    1初始化對(duì)象
    operator new并沒(méi)有進(jìn)行內(nèi)存空間的分配,只是返回之前分配的空間指針。在<new>頭文件中,有一份placement new的默認(rèn)實(shí)現(xiàn)。代碼如下:
// Default placement versions of operator new.
inline void* operator new(std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT
{ return __p; }
inline void* operator new[](std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT
{ return __p; }

2.2.2 delete

c++刪除動(dòng)態(tài)分配的Heap內(nèi)存使用delete。delete的用法也和new一樣存在三種且一一對(duì)應(yīng)。
聲明:

//plain delete
void operator delete(void*) ;
void operator delete[](void*) ;
//nothrow delete
void operator delete(void*, const std::nothrow_t&) ;
void operator delete[](void*, const std::nothrow_t&) ;
//placement delete
inline void operator delete  (void*, void*);
inline void operator delete[](void*, void*) ;

雖然delete依然有三類(lèi)聲明,但是只有plain delete可以通過(guò)delete-expression的方式使用。nothrow delete和placement delete只在相應(yīng)的new-expression拋出異常時(shí)由系統(tǒng)自動(dòng)調(diào)用。如果想手動(dòng)使用nothrow delete和placement delete可以通過(guò)函數(shù)調(diào)用的方式。
nothrow delete和placement delete的函數(shù)使用方式:

operator delete(p, nothrow);
operator delete(buff, p);

2.3 array new與array delete探析

array new和array delete要配對(duì)使用,如果不配對(duì)使用可能出現(xiàn)內(nèi)存泄漏。出現(xiàn)內(nèi)存泄漏的原因并不是因?yàn)閐elete不能完全釋放array new分配的內(nèi)存,而是因?yàn)槭褂胐elete釋放array new分配的內(nèi)存時(shí),編譯器只對(duì)array new分配的數(shù)組的某一個(gè)對(duì)調(diào)用析構(gòu)函數(shù)。這樣就會(huì)導(dǎo)致數(shù)組中的其他對(duì)象因?yàn)闆](méi)有調(diào)用析構(gòu)函數(shù)而導(dǎo)致內(nèi)存沒(méi)有正確釋放。
下面通過(guò)一個(gè)例子來(lái)說(shuō)明這個(gè)問(wèn)題。
代碼:

void ArrayNewAndArrayDeleteTest()
{
    cout << "ArrayNewAndDelete:" << endl;
    String *p1 = new String[3];
    delete p1;
    cout << "ArrayNewAndArrayDelete:" << endl;
    String *p2 = new String[3];
    delete[] p2;
}

運(yùn)行結(jié)果:

****begin run function ArrayNewAndArrayDeleteTest *****
ArrayNewAndDelete:
normal ctor:null string
normal ctor:null string
normal ctor:null string
dtor:
ArrayNewAndArrayDelete:
normal ctor:null string
normal ctor:null string
normal ctor:null string
dtor:
dtor:
dtor:
****end run function ArrayNewAndArrayDeleteTest ****

從結(jié)果可以看出,

  • 使用array new分配數(shù)組p1時(shí)調(diào)用了3次構(gòu)造函數(shù)(3個(gè)normal ctor輸出),使用delete釋放內(nèi)存時(shí)只調(diào)用了一次析構(gòu)函數(shù)(只有一次dtor輸出);
  • 使用array new分配數(shù)組p2時(shí)調(diào)用了3次構(gòu)造函數(shù)(3個(gè)normal ctor輸出),使用array delete釋放內(nèi)存時(shí)調(diào)用了3次析構(gòu)函數(shù)(3個(gè)dtor輸出)
    綜上,array new配合delete,會(huì)導(dǎo)致析構(gòu)函數(shù)只調(diào)用一次,從而導(dǎo)致內(nèi)存泄漏。

3.其他

編譯器按照數(shù)據(jù)成員的聲明順序初始化對(duì)象,而不是按照初始化列表聲明的順序初始化對(duì)象。構(gòu)造函數(shù)的初始化列表最好和類(lèi)的數(shù)據(jù)成員的聲明順序一致,且最好不要使用類(lèi)的其他成員來(lái)初始化類(lèi)的成員。

說(shuō)明:
1.本文使用的String對(duì)象為侯老師上課所用String class,只是對(duì)函數(shù)的調(diào)用添加輸出語(yǔ)句。
2.使用編譯器為g++ 4.8.1
引用:
1.http://www.cnblogs.com/resound/archive/2011/10/27/2226472.html
2.GeekBand課件
3.cpp primer 5th edition
4.http://en.cppreference.com/w/cpp/memory/new/operator_delete
5.http://en.cppreference.com/w/cpp/memory/new/operator_new

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,732評(píng)論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,214評(píng)論 3 426
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 177,781評(píng)論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,588評(píng)論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,315評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,699評(píng)論 1 327
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,698評(píng)論 3 446
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,882評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,441評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,189評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,388評(píng)論 1 372
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,933評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,613評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 35,023評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,310評(píng)論 1 293
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,112評(píng)論 3 398
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,334評(píng)論 2 377

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