C++動(dòng)態(tài)內(nèi)存
了解動(dòng)態(tài)內(nèi)存在 C++ 中是如何工作的是成為一名合格的 C++ 程序員必不可少的。C++ 程序中的內(nèi)存分為兩個(gè)部分:
- 棧:在函數(shù)內(nèi)部聲明的所有變量都將占用棧內(nèi)存。
- 堆:這是程序中未使用的內(nèi)存编丘,在程序運(yùn)行時(shí)可用于動(dòng)態(tài)分配內(nèi)存。
很多時(shí)候,您無(wú)法提前預(yù)知需要多少內(nèi)存來(lái)存儲(chǔ)某個(gè)定義變量中的特定信息,所需內(nèi)存的大小需要在運(yùn)行時(shí)才能確定矛纹。
在 C++ 中,您可以使用特殊的運(yùn)算符為給定類型的變量在運(yùn)行時(shí)分配堆內(nèi)的內(nèi)存光稼,這會(huì)返回所分配的空間地址或南。這種運(yùn)算符即 new 運(yùn)算符。
如果您不再需要?jiǎng)討B(tài)分配的內(nèi)存空間艾君,可以使用 delete 運(yùn)算符采够,刪除之前由 new 運(yùn)算符分配的內(nèi)存。
new和delete運(yùn)算符
new data-type
在這里冰垄,data-type 可以是包括數(shù)組在內(nèi)的任意內(nèi)置的數(shù)據(jù)類型蹬癌,也可以是包括類或結(jié)構(gòu)在內(nèi)的用戶自定義的任何數(shù)據(jù)類型。讓我們先來(lái)看下內(nèi)置的數(shù)據(jù)類型虹茶。例如逝薪,我們可以定義一個(gè)指向 double 類型的指針,然后請(qǐng)求內(nèi)存蝴罪,該內(nèi)存在執(zhí)行時(shí)被分配翼闽。我們可以按照下面的語(yǔ)句使用 new 運(yùn)算符來(lái)完成這點(diǎn):
double* pvalue = NULL; // 初始化為 null 的指針
pvalue = new double; // 為變量請(qǐng)求內(nèi)存
malloc() 函數(shù)在 C 語(yǔ)言中就出現(xiàn)了,在 C++ 中仍然存在洲炊,但建議盡量不要使用 malloc() 函數(shù)。new 與 malloc() 函數(shù)相比尼啡,其主要的優(yōu)點(diǎn)是暂衡,new 不只是分配了內(nèi)存,它還創(chuàng)建了對(duì)象崖瞭。
在任何時(shí)候狂巢,當(dāng)您覺得某個(gè)已經(jīng)動(dòng)態(tài)分配內(nèi)存的變量不再需要使用時(shí),您可以使用 delete 操作符釋放它所占用的內(nèi)存书聚,如下所示:
delete pvalue; // 釋放 pvalue 所指向的內(nèi)存
new和malloc有什么不同
new 的功能是在堆區(qū)新建一個(gè)對(duì)象唧领,并返回該對(duì)象的指針藻雌。所謂的【新建對(duì)象】的意思就是,將調(diào)用該類的構(gòu)造函數(shù)斩个,因?yàn)槿绻粯?gòu)造的話胯杭,就不能稱之為一個(gè)對(duì)象。而 malloc 只是機(jī)械的分配一塊內(nèi)存受啥,如果用 mallco 在堆區(qū)創(chuàng)建一個(gè)對(duì)象的話做个,是不會(huì)調(diào)用構(gòu)造函數(shù)的。嚴(yán)格說(shuō)來(lái)用 malloc 不能算是新建了一個(gè)對(duì)象滚局,只能說(shuō)是分配了一塊與該類對(duì)象匹配的內(nèi)存而已居暖,然后強(qiáng)行把它解釋為【這是一個(gè)對(duì)象】,按這個(gè)邏輯來(lái)藤肢,也不存在構(gòu)造函數(shù)什么事太闺。同樣的,用 delete 去釋放一個(gè)堆區(qū)的對(duì)象嘁圈,會(huì)調(diào)用該對(duì)象的析構(gòu)函數(shù)省骂。用 free 去釋放一個(gè)堆區(qū)的對(duì)象,不會(huì)調(diào)用該對(duì)象的析構(gòu)函數(shù)丑孩。
做個(gè)簡(jiǎn)單的實(shí)驗(yàn)即可明了:
#include <iostream>
#include <malloc.h>
class TEST
{
private:
int num1;
int num2;
public:
TEST()
{
num1 = 10;
num2 = 20;
}
void Print()
{
std::cout << num1 << " " << num2 << std::endl;
}
};
int main(void)
{
// 用malloc()函數(shù)在堆區(qū)分配一塊內(nèi)存空間冀宴,然后用強(qiáng)制類型轉(zhuǎn)換將該塊內(nèi)存空間
// 解釋為是一個(gè)TEST類對(duì)象,這不會(huì)調(diào)用TEST的默認(rèn)構(gòu)造函數(shù)
TEST * pObj1 = (TEST *)malloc(sizeof(TEST));
pObj1->Print();
// 用new在堆區(qū)創(chuàng)建一個(gè)TEST類的對(duì)象温学,這會(huì)調(diào)用TEST類的默認(rèn)構(gòu)造函數(shù)
TEST * pObj2 = new TEST;
pObj2->Print();
return 0;
}
/*
運(yùn)行結(jié)果:
-----------------------------
-842150451 -842150451 |
10 20 |
請(qǐng)按任意鍵繼續(xù). . . |
-----------------------------
我們可以看到pObj1所指的對(duì)象中略贮,字段num1與num2都是垃圾值
而pObj2所指的對(duì)象中,字段num1與num2顯然是經(jīng)過(guò)了構(gòu)造后的值
*/
智能指針
動(dòng)態(tài)內(nèi)存的使用很容易出現(xiàn)問題仗岖,因?yàn)榇_保在正確的時(shí)間釋放內(nèi)存是極其困難的逃延。有時(shí)候我們會(huì)忘記釋放內(nèi)存,在這種情況下就會(huì)產(chǎn)生內(nèi)存泄漏轧拄;有時(shí)候在尚有指針引用內(nèi)存的情況下我們就釋放了它揽祥,在這種情況下就會(huì)產(chǎn)生引用非法內(nèi)存的指針。
為了更容易地使用動(dòng)態(tài)內(nèi)存檩电,新的標(biāo)準(zhǔn)庫(kù)提供了兩種智能指針類型來(lái)管理動(dòng)態(tài)對(duì)象拄丰。智能指針的行為類似常規(guī)指針,重要的區(qū)別是它負(fù)責(zé)自動(dòng)釋放所指向的對(duì)象俐末。新標(biāo)準(zhǔn)庫(kù)提供的這兩種智能指針的區(qū)別在于管理底層指針的方式:shared_ptr允許多個(gè)指針指向同一個(gè)對(duì)象料按;unique_ptr則獨(dú)占所指向的對(duì)象。標(biāo)準(zhǔn)庫(kù)還定義了一個(gè)名為weak_ptr的伴隨類卓箫,它是一種弱引用指向shared_ptr所管理的對(duì)象载矿。這三種類型都定義在memory頭文件中。
shred_ptr類
類似vector烹卒,智能指針也是模板闷盔。因此當(dāng)我們創(chuàng)建一個(gè)智能指針時(shí)弯洗,必須提供額外的信息——指針可以指向的類型。與vector一樣逢勾,我們?cè)诩饫ㄌ?hào)內(nèi)給出類型牡整,之后是所定義的這種智能指針的名字:
shared_ptr<string> p1; //shared_ptr,可以指向string
shared_ptr<list<int>> p2; //shard_ptr,可以指向int的list
默認(rèn)初始化的智能指針中保存著一個(gè)空指針。
智能指針的使用方式與普通指針類似敏沉。解引用一個(gè)智能指針返回它指向的對(duì)象果正。如果在一個(gè)條件判斷中使用智能指針,效果就是檢測(cè)它是否為空:
if(p1 && p1->empty()) //如果p1不為空盟迟,檢查它是否指向一個(gè)空string
*p1 = "hi"; //若果p1指向一個(gè)空string秋泳,解引用p1,將一個(gè)新值賦予string
shared_ptr和unique_ptr | 都支持的操作 |
---|---|
shared_ptr<T> sp | 空智能指針攒菠,可以指向類型為T的對(duì)象 |
unique_ptr<T> up | |
p | 將p用作一個(gè)條件判斷迫皱,若p指向一個(gè)對(duì)象,則為true |
*p | 解引用p辖众,獲得它所指的對(duì)象 |
p-> | 等價(jià)于(*p).mem |
p.get() | 返回p中所保存的指針.要小心,若智能指針釋放了其對(duì)象,返回的指針?biāo)赶虻膶?duì)象也就消失了 |
swap(p,q) | 交換p和q中的指針 |
p.swap(q) |