12.1 智能指針
智能指針行為類似普通指針,但它負責自動釋放所知的對象。
#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中保存的指針,與對象的引用計數無關,不要使用它來初始化一個智能指針或賦值
swap(p,q);//交換p,q中的指針
p.swap(q);
shared_ptr獨有的操作
make_shared<T>(args);//返回一個shared_ptr,指向一個動態分配的類型為T的對象,使用args初始化此對象
shared_ptr<T> p(q);//p是q的拷貝;遞增q中的引用計數,q中的指針必須可轉換為T*
p = q;//pq保存的指針需可以互相轉化,遞減p的引用計數,遞增q的引用計數;當p的引用計數為0,會將管理的原內存釋放
p.use_count();//返回與p共享的對象的智能指針個數。
p.unique();//若p.use_count()返回1,則返回true。
make_shared函數會在動態內存中分配一個對象并初始化。
傳遞的參數必須符合類型的某個構造函數;內置類型若不提供參數,將進行值初始化。
shared_ptr的拷貝auto p(q);
拷貝操作將會遞增引用計數的值:
1,用一個shared_ptr初始化另一個shared_ptr
2,將其作為參數傳遞給一個函數
3,作為函數的返回值
shared_ptr的賦值和銷毀,r = q
1,賦值
2,shared_ptr被銷毀
以上操作會遞減指針引用計數的值,當計數值為0時釋放所管理的對象(shared_ptr的析構函數)
12.1.2 直接管理內存
int *pi = new int;//內置類型和組合類型的對象的值未定義,默認初始化
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);//根據obj對象推斷類型,并用obj初始化,obj只可以擁有一個
動態分配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;//銷毀給定指針指向的對象,釋放對應的內存
p=nullptr;//重置指針,避免成為空懸指針
釋放非new分配的內存或多次釋放行為未定義。
12.1.3 shared_ptr和new結合使用
shared_ptr<int> p(new int(42));//接受指針參數的智能指針的構造函數是explicit的,不可將一個內置指針隱式轉換為智能指針,必須進行直接初始化
默認情況下,初始化智能指針的普通指針必須指向動態分配的內存(使用delete釋放),但可以提供其他代替delete的操作來將智能指針綁定到指向其他類型的指針上。
shared_ptr<T> p(q);//p管理內置指針q(new分配)所指的對象,q可轉換為T*
shared_ptr<T>p(u);//p從unique_ptr接管對象,u置空
shared_ptr<T>p(q, d);//p結構內置指針q指向的對象,使用可調用對象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置空;若傳遞了內置指針q,則令p指向q;若還有參數d,會調用d來釋放q;會更新引用計數。
共享對象的智能指針的處理:
if (!p.unique()){
p.reset(new string(*p));//p不是唯一指向對象的指針,但此時要改變p指向的元素,必須創建一個拷貝
}
//對p的對象進行操作
12.1.4 智能指針和異常
1,不使用相同的內置指針初始化(或reset)多個智能指針
2,不delete get()返回的指針
3,不使用get() 初始化或reset另一個智能指針
4,使用get()返回的指針,當其對應的最后一個智能指針銷毀后,get()返回的指針無效了
5,使用智能指針管理不是有new分配的內存,需要有附加的刪除器
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的可調用對象釋放指針
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放棄對指針的控制權,返回指針,并將u置空;可用來初始化另一個智能指針或賦值
u.reset();//釋放u所指的對象
u.reset(q);//釋放u所指的對象,u指向這個內置指針綁定的對象
u.reset(nullptr);//釋放u所指的對象,將u置空;
release和reset可以將對象的所有權轉移到另一個unique_ptr上。
unique_ptr<string> p(p1.release());//p1置空,p管理p1的對象
unique_ptr<string> p2(new string("haha"));
p.reset(p2.release());//p釋放了原指向的內存,重新指向了p2的內存,p2為空
可以拷貝或賦值一個將要被銷毀的unique_ptr,比如從函數返回一個unique_ptr或返回局部unique_ptr的拷貝;
12.1.6 weak_ptr
和某些shared_ptr共享同一個對象,但不會增加shared_ptr的引用計數。weak_ptr不會控制對象的生存期。
weak_ptr<T> w;
weak_ptr<T> w(sp);//w和sp(一個shared_ptr)指向相同的對象,T必須是可以轉換為sp指向的類型。
w = p;//p可以是shared_ptr或weak_ptr,賦值后w,p共享對象
w.reset();//將w置為空
w.use_count();//與w共享對象的shared_ptr數量
w.expired();//若w.use_count()為0,則為true
w.lock();//若w.expired()返回true,則返回空的shared_ptr;否則返回一個w指向對象的shared_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 動態數組
使用new T[] 和delete[]
T *p = new T[n];//n必須是整型,不必是常量。n可以為0,返回合法的非空指針。
typedef int ?arrInt[100];
int *p = new arrInt;
注意p是元素的指針而不是數組的指針,并且嚴格說動態數組非數組,只是一段有類型的連續的存。
默認情況下,創建的動態數組執行默認初始化;
int *p1 = new int[10];//值未定義,默認初始化
int *p2 = new int[10]();//值初始化,0
直接初始化
int *p3 = new int[10]{1,2,3,4,5,6,7,8};
delete [] p1;//添加[]和去掉[],需要看p1是單個元素的指針還是動態數組的指針,否則delete操作未定義
可直接使用unique_ptr管理動態數組
unique_ptr<int[]> up(new int[10]);
up.release();//自動調用delete[]銷毀
指向數組的unique_ptr不支持成員訪問運算符。
unique_ptr<T[]> u;
unique_ptr<T[]> u(p);//內置指針p指向動態數組,p必須可以轉換為類型T*
up[i];//需使用下標來訪問
shared_ptr不支持直接管理動態數組,但可以定義自己的刪除器,間接管理。
shared_ptr<int> sp(new int[10], [] (int *p){ delete [] p;});
sp.reset();//使用定義時的lambda作為刪除器
sp.get();//借用此指針訪問動態數組的值
12.2.2 allocator 類
#include<memory>
allocator類將內存分配和對象構造分離出來,類型感知的內存分配方法,分配的內存是原始的,未分配的。
allocator<T> a;//a可以為類型為T的對象分配內存
auto p = a.allocator(n);//分配n個未經構造的內存,保存n個類型為T的對象
a.construct(p, args);//p是一個類型為T*的指針,指向一塊原始內存;args被傳遞給類型為T的構造函數,args為參數列表
a.destory(p);//p為類型為T*的指針,對p所指對象執行析構函數,必須是構造過得
a.deallocator(p,n);//p是由allocator返回的指針,n是創建時的大小;釋放從p開始的n個類型為T的對象,在此之前必須為每個內存調用destory;
拷貝和填充未初始化內存
uninitialized_copy(b,e,b2);//將范圍[b,e)的元素拷貝到b2指向的未構造的內存
uninitialized_copy_n(b,n,b2);//
uninitialized_fill(b,e,t);//在[b,e)指向的原始內存開始創建n個對象,對象的值均為t的拷貝
uninitialized_fill_n(b,n,t);
返回指向最后一個構造的元素的下一位置。