先簡單介紹一下咱們經常使用的幾種內存:靜態內存、棧內存、動態內存
1)從靜態內存分配空間,內存在程序編譯的時候就已經被分配好,好比全局變量和static變量
2)棧內存是在執行函數時,函數內部定義局部變量所分配的內存,在函數結束時,這些內存就被釋放,效率比較高,但棧空間每每較小
3)動態內存,又稱堆,由程序員自主經過malloc或new來分配,經過free和delete來進行釋放。若是忘記釋放,就會形成內存泄漏;若是在尚有指針引用內存的狀況下釋放內存,就會產生引用非法內存的指針程序員
爲了更容易地使用動態內存,C++提供了兩種智能指針類型來管理動態對象。智能指針的行爲相似常規指針,可是它們能夠自動釋放所指向的對象。shared_ptr容許多個指針指向同一個對象,unique_ptr則獨佔所指向的對象。算法
1. shared_ptr
頭文件:memory
智能指針是模板類。其建立方法以下:數組
shared_ptr<string> p1; shared_ptr<vector<int>> p2;
默認初始化爲一個空指針。函數
智能指針的使用方式和普通指針相似:指針
if(p1 && p1->empty()) //若是p1爲空,則將「hi」賦予string *p1 = "h1";
還有其餘一些用法:
1)shared_ptr和unique_ptr都支持的用法code
p.get();//返回p中保存的指針 swap(p,q);//交換p和q中指針 q.swap(p);
2)僅有shared_ptr支持的操做對象
make_shared<T>(args);//返回一個shared_ptr指向一個T對象,並用args初始化對象 shared_ptr<T> p(q);//p是q的拷貝,此操做遞增q中計數器 p = q;//遞減p中計數器,遞增q中計數器 p.unique();//若p.use_count()爲1,返回true,不然false p.use_count();//返回指針的引用計數,一旦計數爲0,指針會釋放本身管理的對象
使用動態內存的緣由:
1)程序不知道本身須要使用多少對象
2)程序不知道所需對象的準確類型「
3)程序須要在多個對象之間共享數據內存
下面咱們看一個處於第3個緣由使用動態內存的例子:字符串
template<typename T> class Test{ public: Test():data(make_shared<vector<T>>()){} Test(std::initiallizer_list<T> li):data(make_shared<vector<T>>(li)){} void pop_back() {data->pop_back();} T& top() const {return data->back();} bool empty() const {return data->empty();} size_t size() const {return data->size();} void push_back(const T& d){data->push_back(d);} private: shared_ptr<vector<T>> data; };
使用new和delete管理動態內存的一些實例
由於這個在以前已經學過,簡單的列幾個例子:get
string *s = new string; string *s = new string("2333"); auto p = new auto(obj);//括號中必須只有單一初始化器才能這麼用 auto p = new auto({a,b});//錯誤用法 //正常狀況下,new失敗會拋出std::bad_alloc異常 int * p = new (nothrow) int;//若分配失敗,返回空指針 delete p; delete []p; shared_ptr和new結合使用 shared_ptr<int> p(new int(10));//正確用法 shared_ptr<int> p = new int();//錯誤用法 shared_ptr<int> f(){ return new int;//錯誤用法 } shared_ptr<int> f(){ returb shared_ptr<int>(new int);//正確用法 }
定義和改變shared_ptr的其餘辦法
1)shared_ptr<T> p(q);
p管理內置指針q所指向的對象,q必須指向new分配的內存,且能轉換成T×類型
2)shared_ptr<T> p(u);
p從unique_ptr u哪裏接管對象的全部權,並將u置爲空
3)shared_ptr<T> p(q,d);
p接管內置指針q所指向對象的全部權,p將使用可調用對象d來代替delete
4)shared_ptr<T> p(p2,d);
p是shared_ptr p2的拷貝,並p調用可調用對象d來代替delete
5)
p.reset();
若是p是惟一指向對象的shared_ptr,reset會釋放此對象
p.reset(q);
若傳遞了可選的參數內置指針q,會令p指向q,不然將p置空
p.reset(q,d);
若還傳遞了可選參數d,將會調用d而不是delete釋放q
注意:雖然C++提供了內置指針和智能指針的轉換,可是請儘可能避免兩種指針混合使用
2. unique_ptr
與shared_ptr不一樣的是,某個時刻只能有一個unique_ptr指向一個給定對象,當ptr被銷燬時,它所指向的對象也被銷燬
其基本操做在前面已經列出,與shared_ptr不一樣的是,定義一個unique ptr時,必須將它綁定到一個new返回的指針上
unique_ptr<double> p1; unique_ptr<int> p2(new int(43));
由於一個unique擁有一個對象,因此它不支持普通的拷貝或賦值操做
unique_ptr支持的其餘操做
1)unique_ptr<T,D> u2;
定義一個空的u2,u2會使用一個類型爲D的可調用對象來釋放它的指針
2)unique_ptr<T,D> u(d);
定義一個空的u,指向類型爲T的對象,用類型爲D的對象d代替delete
3)u = nullptr;
釋放u指向的對象,將u設爲空
4)u.release();
u放棄對指針的全部權,返回指針,並將u置爲空
5)
u.reset();
釋放u指向的對象
u,reset(q);
若是提供了內置指針q,則令u指向這個對象,不然將u置空
u.reset(nullptr);
3.weak_ptr
weak_ptr是一種不控制所指向對象生存週期的智能指針,它指向一個由shared_ptr管理的對象,將一個weak_ptr綁定到shared_ptr不會改變shared_ptr的引用計數
用法:
weak_ptr<T> w; weak_ptr<T> w(sp); w = p;//p 爲weak ptr或者shared ptr w.reset();//將w置爲空 w.use_count(); w.expired();//若w.use_count()爲0,則爲true,不然false w.lock();//返回shared_ptr,或nullptr
new和delete只能一次分配釋放一個對象,一些場合,咱們須要一次爲不少對象分配內存,爲了支持這種需求,C+=和標準庫提供樂兩種方法:new和allocator
1. new和數組
一些基本語法:
int *a = new int[get_size()]; int *a = new string[100](); int *a = new string[10]{"a","b","c",string(3,'x')}; typedef int att[10]; int *a = new att; delete []a;
智能指針和動態數組
能夠用unique_ptr管理動態數組,語法以下:
unique_ptr<int[]> up(new int[10]); up[i] = 10; up.release();//自動調用delete[]釋放指針
此時
指向數組的unique ptr不支持成員訪問運算符即 點 和 箭頭 運算符
其餘的unique ptr操做保持不變
與unique_ptr不一樣,shared_ptr不支持直接管理動態數組,須要本身ingoing刪除器
shared_ptr<int> sp(new int[10],[](int *p){delete []p;});//此處使用了lambda表達式 shared_ptr未定義下標運算符,訪問數組元素很麻煩 *(sp.get()+i) = i; //i爲咱們要訪問的下標
2.allocator類
new和delete有一些靈活性的侷限,它們將多個對象的構造和內存分配綁定在一塊兒,有時這會形成浪費
allocater類定義在頭文件memory中,它幫助咱們將對象構造和內存分配分離凱,它提供一種類型感知的內存分配方法,它分配的內存是原始的,未構造的
基本用法:
allocator<T> a;//定義allocator對象,它能夠爲類型爲T的對象分配內存 auto p = a.allocate(n);//分配一段原始的、未構造的內存,保存n個類型爲T的對象 a.deallocate(p,n);//釋放從T×指針 p中地址開始的內存,n必須是a.allocate(n)中的n,在這以前, 用戶須要對在這塊內存中建立的對象調用destroy a.construct(p,args);//在p指向的內存中構造一個對象 a.destroy(p);//對p指向的對象執行析構函數
例如,爲string分配內存、構造對象、釋放對象:
allocator<string> alloc; auto const p = alloc.allocate(n);//分配n個未初始化的string auto q = p; alloc.construct(q++);//*q爲空字符串 alloc.construct(q++,10,'c');//*q爲cccccccccc alloc.construct(q++,"h1");//*q爲hi while(q!=p) alloc.destroy(q--);//被銷燬後,能夠從新構造 alloc.deallocate(p,n);
標準庫還爲allocator類定義了兩個伴隨算法,用來進行拷貝和填充操做:
uninitialized_copy(b,e,b2);//從迭代器b和e指出的範圍中拷貝元素到迭代器b2開始的未構造內存中 uninitialized_copy(b,n,b2);//將從迭代其b開始n個元素拷貝到迭代器b2開始的內存中 uninitialized_fill(b,e,t);//在迭代器b和e指出的範圍中建立t的拷貝對象(多個) uninitialized_fill(b,n,t);//從b開始的內存中建立n個t對象