目錄
前言
1.new
2.delete
3.底層邏輯
4.定位new
5.對(duì)比
前言
??之前在使用C語(yǔ)言的時(shí)候,我們便使用?malloc?和?calloc?等函數(shù)進(jìn)行動(dòng)態(tài)內(nèi)存的開(kāi)辟。但?C++?之中又引入了兩個(gè)操作符?new?和?delete?來(lái)代替C語(yǔ)言中的函數(shù)進(jìn)行動(dòng)態(tài)內(nèi)存的管理。下面就一起來(lái)學(xué)習(xí)如何使用吧。
1.new
??使用?new?比使用?malloc?方便許多,只需要?new+?類(lèi)型即可完成空間申請(qǐng)。而隨著后面加的操作不同,能達(dá)到不一樣的效果。
??不僅如此使用?new?之后不需要像?malloc?那樣檢查是否開(kāi)辟成功,new?失敗會(huì)自動(dòng)拋異常(具體如何,以后再進(jìn)行講解)。
int main()
{
int* p1 = new int; //申請(qǐng)一個(gè)int大小的空間
int* p2 = new int(3); //申請(qǐng)一個(gè)int大小的空間并初始化成3
int* p3 = new int[5]; //申請(qǐng)五個(gè)int大小的空間
int* p4 = new int[5]{ 1,2,3,4,5 }; //申請(qǐng)五個(gè)int大小的空間并初始化
return 0;
}
???雖然說(shuō)?C++?中同樣支持使用C語(yǔ)言的?malloc?,但在有些情況下會(huì)顯得有些捉襟見(jiàn)肘,比如以下代碼:
class A //定義類(lèi)A
{
public:
A(int a = 0, int b = 0)
:_a(a)
, _b(b)
{}
private:
int _a;
int _b;
};
int main()
{
A* p1 = new A(2, 3); //用new動(dòng)態(tài)開(kāi)辟
A* p2 = (A*)malloc(sizeof(A)); //用malloc動(dòng)態(tài)開(kāi)辟
if (p2 == NULL)
{
perror("malloc");
return 0;
}
return 0;
}
???通過(guò)調(diào)試我們可以看到,使用?new?開(kāi)辟出來(lái)的空間是已經(jīng)初始化好的,而使用?malloc?開(kāi)辟的空間中則是隨機(jī)值。
??這是由于?new?對(duì)于自定義類(lèi)型會(huì)調(diào)用其構(gòu)造函數(shù)進(jìn)行對(duì)象的初始化。若一次申請(qǐng)?n?個(gè)對(duì)象的連續(xù),便會(huì)調(diào)用?n?次構(gòu)造函數(shù)進(jìn)行初始化。
2.delete
??有開(kāi)辟就有釋放,delete?就是進(jìn)行釋放這個(gè)工作。只需要?delete?加空間的首指針便可完成釋放,若釋放多個(gè)對(duì)象的空間則需要使用?delete[ ]。
int main()
{
int* p1 = new int; //申請(qǐng)一個(gè)int大小的空間
int* p2 = new int(3); //申請(qǐng)一個(gè)int大小的空間并初始化成3
int* p3 = new int[5]; //申請(qǐng)五個(gè)int大小的空間
delete p1; //正常申請(qǐng)空間則直接delete
delete p2;
delete[] p3; //帶[]申請(qǐng)則也需要用delete[]進(jìn)行釋放
return 0;
}
??直接使用?delete[ ]?,沒(méi)有告訴系統(tǒng)空間多大那系統(tǒng)是如何釋放空間的呢?
??編譯器在?new?的時(shí)候,若遇上多組開(kāi)辟的情形時(shí),會(huì)在開(kāi)辟的空間前額外增加一個(gè)位置用于存儲(chǔ)開(kāi)辟空間的大小。
??根據(jù)上文推導(dǎo),delete?應(yīng)該會(huì)調(diào)用類(lèi)的析構(gòu)函數(shù)進(jìn)行回收。我們可以使用下面的代碼進(jìn)行驗(yàn)證一下。
class A
{
public:
A(int a = 7, int b = 8)
:_a(a)
, _b(b)
{
cout << "A()" << endl;
}
~A()
{
cout << "~A()";
}
private:
int _a;
int _b;
};
int main()
{
A* p1 = new A();
delete p1;
return 0;
}
??在當(dāng)前代碼中,只有使用了?delete?才會(huì)調(diào)用析構(gòu)函數(shù),而?free?只是單純的釋放空間。若在類(lèi)之中再次進(jìn)行動(dòng)態(tài)開(kāi)辟,而不調(diào)用析構(gòu)函數(shù)進(jìn)行回收時(shí),便會(huì)出現(xiàn)內(nèi)存泄漏。
class A
{
public:
A(int a = 0, int b = 0)
:_a(a)
, _b(new int(b))
{
cout << "A()" << endl;
}
~A()
{
delete _b;
cout << "~A()";
}
private:
int _a;
int* _b;
};
int main()
{
A* p1 = new A();
return 0;
}
??若在平時(shí)的工程之中引發(fā)內(nèi)存泄漏,便會(huì)降低代碼的運(yùn)行效率。因此切記,new?和?delete?必須要配合使用。?
3.底層邏輯
??通過(guò)查看系統(tǒng)文檔,我們便能夠找到兩個(gè)全局函數(shù)。
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
// try to allocate size bytes
void *p;
while ((p = malloc(size)) == 0)
if (_callnewh(size) == 0)
{
static const std::bad_alloc nomem;
_RAISE(nomem);
}
return (p);
}
void operator delete(void* pUserData)
{
_CrtMemBlockHeader* pHead;
RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
if (pUserData == NULL)
return;
_mlock(_HEAP_LOCK);
__TRY
pHead = pHdr(pUserData);
_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
_free_dbg(pUserData, pHead->nBlockUse);
__FINALLY
_munlock(_HEAP_LOCK);
__END_TRY_FINALLY
return;
}
#define free(p) _free_dbg(p, _NORMAL_BLOCK)
???new?和 delete?是用戶(hù)進(jìn)行動(dòng)態(tài)內(nèi)存申請(qǐng)和釋放的操作符,operator new 和?operator delete 是系統(tǒng)提供的全局函數(shù),new?在底層調(diào)用?operator new?全局函數(shù)來(lái)申請(qǐng)空間,delete?在底層通過(guò)?operator delete?全局函數(shù)來(lái)釋放空間。
??由上述的底層代碼我們發(fā)現(xiàn),operator new 實(shí)際也是通過(guò)?malloc?來(lái)申請(qǐng)空間,如果申請(qǐng)空間成功就直接返回,否則就拋異常。而operator delete 最終是通過(guò)?free?來(lái)釋放空間的
??值得注意的是,operator new 和?operator delete?是系統(tǒng)提供的全局函數(shù),并不是運(yùn)算符重載,只是恰好與其用了相同的名字。
??new?和 delete?的本質(zhì)上就是對(duì)?malloc?和?free?的封裝。
??new?便是先開(kāi)辟空間再調(diào)用構(gòu)造函數(shù),而?delete?則是先調(diào)用析構(gòu)函數(shù)再釋放空間。
4.定位new
??定位?new?表達(dá)式是在已分配的原始內(nèi)存空間中調(diào)用構(gòu)造函數(shù)初始化一個(gè)對(duì)象。
class A
{
public:
A(int a = 0)
:_a(a)
{}
~A()
{}
private:
int _a;
};
int main()
{
A* p = (A*)malloc(sizeof(A));
p = new(p)A; //如果構(gòu)造函數(shù)有參數(shù)則需要傳參2
return 0;
}
??這個(gè)操作一般是不會(huì)用到的,因?yàn)檎G闆r下我們直接使用?new?就能達(dá)到這個(gè)結(jié)果,沒(méi)有必要特意?malloc?一次再使用?new?。真正需要使用定位?new 的情況是當(dāng)我們從內(nèi)存池中申請(qǐng)空間后,由于這種申請(qǐng)出來(lái)的空間是未初始化的,因此需要調(diào)用構(gòu)造函數(shù),所以使用定位new進(jìn)行初始化。
5.對(duì)比
- malloc?和 free是函數(shù),new?和 delete?是操作符。
- malloc?申請(qǐng)的空間不會(huì)初始化,new?可以初始化。
- malloc?申請(qǐng)空間時(shí),需要手動(dòng)計(jì)算空間大小并傳遞,new?只需在其后跟上空間的類(lèi)型即可,如果是多個(gè)對(duì)象,即在[ ]中指定對(duì)象個(gè)數(shù)。
- malloc?的返回值為?void*?, 在使用時(shí)必須強(qiáng)轉(zhuǎn),new?不需要。
- malloc?申請(qǐng)空間失敗時(shí),返回的是?NULL?,因此使用時(shí)必須判空,new?不需要,new?失敗則會(huì)進(jìn)行拋異常。
- 申請(qǐng)自定義類(lèi)型對(duì)象時(shí),malloc/free?只會(huì)開(kāi)辟和清理空間,不會(huì)調(diào)用構(gòu)造函數(shù)與析構(gòu)函數(shù),而?new?在申請(qǐng)空間后會(huì)調(diào)用構(gòu)造函數(shù)完成對(duì)象的初始化,?delete?在釋放空間前會(huì)調(diào)用析構(gòu)函數(shù)完成空間中資源的清理。
??不要將?new/delete/malloc/free?混在一起使用,否則在特定的情況下便會(huì)導(dǎo)致程序崩潰。因此,最好將其區(qū)分使用,使用?new?就用?delete?使用?malloc?就用?free?釋放。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-417584.html
??好了,今天C++內(nèi)存管理的講解到這里就結(jié)束了,如果這篇文章對(duì)你有用的話(huà)還請(qǐng)留下你的三連加關(guān)注。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-417584.html
到了這里,關(guān)于我現(xiàn)在必須new一個(gè)對(duì)象!??!的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!