12.1 智能指針
智能指針行為類似普通指針休讳,但它負(fù)責(zé)自動釋放所知的對象主届。
#include <memory>
shared_ptr : 允許多個指針指向同一個對象赵哲,每個指針都會記錄有多少個其他指針指向相同的對象
unique_ptr : 某個對象只允許一個指針指向它
weak_ptr : 弱引用的伴隨類,指向shared_ptr所管理的對象君丁。
shared_ptr 和 unique_ptr支持的操作
shared_ptr<T> sp;//空指針枫夺,可以指向類型為T的對象
unique_ptr<T> up;
if (p){}//若p指向一個對象,條件為true
*p;//解引用p绘闷,獲取其指向的對象
p->mem;//等價于(*p).mem
p.get();//返回p中保存的指針橡庞,與對象的引用計數(shù)無關(guān),不要使用它來初始化一個智能指針或賦值
swap(p,q);//交換p,q中的指針
p.swap(q);
shared_ptr獨有的操作
make_shared<T>(args);//返回一個shared_ptr印蔗,指向一個動態(tài)分配的類型為T的對象扒最,使用args初始化此對象
shared_ptr<T> p(q);//p是q的拷貝;遞增q中的引用計數(shù)华嘹,q中的指針必須可轉(zhuǎn)換為T*
p = q;//pq保存的指針需可以互相轉(zhuǎn)化吧趣,遞減p的引用計數(shù),遞增q的引用計數(shù)耙厚;當(dāng)p的引用計數(shù)為0强挫,會將管理的原內(nèi)存釋放
p.use_count();//返回與p共享的對象的智能指針個數(shù)。
p.unique();//若p.use_count()返回1薛躬,則返回true俯渤。
make_shared函數(shù)會在動態(tài)內(nèi)存中分配一個對象并初始化。
傳遞的參數(shù)必須符合類型的某個構(gòu)造函數(shù)泛豪;內(nèi)置類型若不提供參數(shù)稠诲,將進行值初始化。
shared_ptr的拷貝auto p(q);
拷貝操作將會遞增引用計數(shù)的值:
1诡曙,用一個shared_ptr初始化另一個shared_ptr
2臀叙,將其作為參數(shù)傳遞給一個函數(shù)
3,作為函數(shù)的返回值
shared_ptr的賦值和銷毀价卤,r = q
1劝萤,賦值
2,shared_ptr被銷毀
以上操作會遞減指針引用計數(shù)的值慎璧,當(dāng)計數(shù)值為0時釋放所管理的對象(shared_ptr的析構(gòu)函數(shù))
12.1.2 直接管理內(nèi)存
int *pi = new int;//內(nèi)置類型和組合類型的對象的值未定義床嫌,默認(rèn)初始化
string *ps = new string;
int *pi = new int(1024);//直接初始化
string *ps = new string(10,'9');
vector<int> *p = new vector<int>{0,1,2,3,4,5,6,7,8,9};
int *pi = new int();//值初始化
string *ps = new string();
初始化器
auto p1= new auto(obj);//根據(jù)obj對象推斷類型,并用obj初始化胸私,obj只可以擁有一個
動態(tài)分配const對象
const int *p=new const int(1024);//需初始化
const string *p = new const string;
int *p=new int;//分配失敗厌处,拋出std::bad_alloc
int *p=new (nothrow) int;//分配失敗,返回空指針
delete p;//銷毀給定指針指向的對象岁疼,釋放對應(yīng)的內(nèi)存
p=nullptr;//重置指針阔涉,避免成為空懸指針
釋放非new分配的內(nèi)存或多次釋放行為未定義。
12.1.3 shared_ptr和new結(jié)合使用
shared_ptr<int> p(new int(42));//接受指針參數(shù)的智能指針的構(gòu)造函數(shù)是explicit的,不可將一個內(nèi)置指針隱式轉(zhuǎn)換為智能指針瑰排,必須進行直接初始化
默認(rèn)情況下贯要,初始化智能指針的普通指針必須指向動態(tài)分配的內(nèi)存(使用delete釋放),但可以提供其他代替delete的操作來將智能指針綁定到指向其他類型的指針上椭住。
shared_ptr<T> p(q);//p管理內(nèi)置指針q(new分配)所指的對象崇渗,q可轉(zhuǎn)換為T*
shared_ptr<T>p(u);//p從unique_ptr接管對象,u置空
shared_ptr<T>p(q, d);//p結(jié)構(gòu)內(nèi)置指針q指向的對象京郑,使用可調(diào)用對象d來代替delete
shared_ptr<T>p(p1,d);//p是p1(shared_ptr)的拷貝宅广,但使用d代替delete?
p.reset();
p.reset(q);
p.reset(q,d);
若p是唯一指向其對象的shared_ptr,reset會釋放此對象傻挂,將p置空乘碑;若傳遞了內(nèi)置指針q,則令p指向q金拒;若還有參數(shù)d兽肤,會調(diào)用d來釋放q;會更新引用計數(shù)绪抛。
共享對象的智能指針的處理:
if (!p.unique()){
p.reset(new string(*p));//p不是唯一指向?qū)ο蟮闹羔樧收。藭r要改變p指向的元素,必須創(chuàng)建一個拷貝
}
//對p的對象進行操作
12.1.4 智能指針和異常
1幢码,不使用相同的內(nèi)置指針初始化(或reset)多個智能指針
2笤休,不delete get()返回的指針
3,不使用get() 初始化或reset另一個智能指針
4症副,使用get()返回的指針店雅,當(dāng)其對應(yīng)的最后一個智能指針銷毀后,get()返回的指針無效了
5贞铣,使用智能指針管理不是有new分配的內(nèi)存闹啦,需要有附加的刪除器
12.1.5 unique_ptr
某個時刻只有一個unique_ptr指向一個對象,unique_ptr被銷毀時辕坝,對象也被銷毀窍奋。
定義的同時必須初始化,必須采用直接初始化酱畅。不支持拷貝和賦值
unique_ptr<int> p4;
unique_ptr<int> p1(new int(1024));
int *p2=new int(1203);
unique_ptr<int> p3(p2);
unique_ptr特有的操作
unique_ptr<T> u1;//
unique_ptr<T, D> u2;//u2使用類型為D的可調(diào)用對象釋放指針
unique_ptr<T, D> u(d);//使用類型為D的對象代替delete
unique_ptr<T, D> u(p, d);//使用普通指針p初始化u琳袄,銷毀時使用D類型的對象代替delete
u = nullptr;//釋放u所指的對象,將u置空
u.release();//u放棄對指針的控制權(quán)纺酸,返回指針窖逗,并將u置空;可用來初始化另一個智能指針或賦值
u.reset();//釋放u所指的對象
u.reset(q);//釋放u所指的對象餐蔬,u指向這個內(nèi)置指針綁定的對象
u.reset(nullptr);//釋放u所指的對象滑负,將u置空在张;
release和reset可以將對象的所有權(quán)轉(zhuǎn)移到另一個unique_ptr上。
unique_ptr<string> p(p1.release());//p1置空矮慕,p管理p1的對象
unique_ptr<string> p2(new string("haha"));
p.reset(p2.release());//p釋放了原指向的內(nèi)存,重新指向了p2的內(nèi)存啄骇,p2為空
可以拷貝或賦值一個將要被銷毀的unique_ptr痴鳄,比如從函數(shù)返回一個unique_ptr或返回局部unique_ptr的拷貝;
12.1.6 weak_ptr
和某些shared_ptr共享同一個對象缸夹,但不會增加shared_ptr的引用計數(shù)痪寻。weak_ptr不會控制對象的生存期。
weak_ptr<T> w;
weak_ptr<T> w(sp);//w和sp(一個shared_ptr)指向相同的對象虽惭,T必須是可以轉(zhuǎn)換為sp指向的類型橡类。
w = p;//p可以是shared_ptr或weak_ptr,賦值后w,p共享對象
w.reset();//將w置為空
w.use_count();//與w共享對象的shared_ptr數(shù)量
w.expired();//若w.use_count()為0芽唇,則為true
w.lock();//若w.expired()返回true顾画,則返回空的shared_ptr;否則返回一個w指向?qū)ο蟮膕hared_ptr
不可用weak_ptr直接返回對象匆笤,必須用lock研侣;
auto p = make_shared<int>(100);
weak_ptr<int> wp(p);
if (shared_ptr<int> np = wp.lock()){
//np和p共享同一個對象
}
12.2 動態(tài)數(shù)組
使用new T[] 和delete[]
T *p = new T[n];//n必須是整型,不必是常量炮捧。n可以為0庶诡,返回合法的非空指針。
typedef int ?arrInt[100];
int *p = new arrInt;
注意p是元素的指針而不是數(shù)組的指針咆课,并且嚴(yán)格說動態(tài)數(shù)組非數(shù)組末誓,只是一段有類型的連續(xù)的存。
默認(rèn)情況下书蚪,創(chuàng)建的動態(tài)數(shù)組執(zhí)行默認(rèn)初始化喇澡;
int *p1 = new int[10];//值未定義,默認(rèn)初始化
int *p2 = new int[10]();//值初始化,0
直接初始化
int *p3 = new int[10]{1,2,3,4,5,6,7,8};
delete [] p1;//添加[]和去掉[]善炫,需要看p1是單個元素的指針還是動態(tài)數(shù)組的指針撩幽,否則delete操作未定義
可直接使用unique_ptr管理動態(tài)數(shù)組
unique_ptr<int[]> up(new int[10]);
up.release();//自動調(diào)用delete[]銷毀
指向數(shù)組的unique_ptr不支持成員訪問運算符。
unique_ptr<T[]> u;
unique_ptr<T[]> u(p);//內(nèi)置指針p指向動態(tài)數(shù)組箩艺,p必須可以轉(zhuǎn)換為類型T*
up[i];//需使用下標(biāo)來訪問
shared_ptr不支持直接管理動態(tài)數(shù)組窜醉,但可以定義自己的刪除器,間接管理艺谆。
shared_ptr<int> sp(new int[10], [] (int *p){ delete [] p;});
sp.reset();//使用定義時的lambda作為刪除器
sp.get();//借用此指針訪問動態(tài)數(shù)組的值
12.2.2 allocator 類
#include<memory>
allocator類將內(nèi)存分配和對象構(gòu)造分離出來榨惰,類型感知的內(nèi)存分配方法,分配的內(nèi)存是原始的静汤,未分配的琅催。
allocator<T> a;//a可以為類型為T的對象分配內(nèi)存
auto p = a.allocator(n);//分配n個未經(jīng)構(gòu)造的內(nèi)存居凶,保存n個類型為T的對象
a.construct(p, args);//p是一個類型為T*的指針,指向一塊原始內(nèi)存藤抡;args被傳遞給類型為T的構(gòu)造函數(shù)侠碧,args為參數(shù)列表
a.destory(p);//p為類型為T*的指針,對p所指對象執(zhí)行析構(gòu)函數(shù)缠黍,必須是構(gòu)造過得
a.deallocator(p,n);//p是由allocator返回的指針弄兜,n是創(chuàng)建時的大小瓷式;釋放從p開始的n個類型為T的對象替饿,在此之前必須為每個內(nèi)存調(diào)用destory;
拷貝和填充未初始化內(nèi)存
uninitialized_copy(b,e,b2);//將范圍[b贸典,e)的元素拷貝到b2指向的未構(gòu)造的內(nèi)存
uninitialized_copy_n(b,n,b2);//
uninitialized_fill(b,e,t);//在[b视卢,e)指向的原始內(nèi)存開始創(chuàng)建n個對象,對象的值均為t的拷貝
uninitialized_fill_n(b,n,t);
返回指向最后一個構(gòu)造的元素的下一位置廊驼。