四、智能指針
Author: XFFer_
文章目錄
01 直接內存管理(new/delete)、創建新工程觀察內存泄漏
1 直接內存管理(new/delete)
2 創建新工程,觀察內存泄漏
直接內存管理(new/delete)
C語言中是
malloc/free
定義初值
int *pointi = new int(100);
string *points = new string(5, 'a'); //"aaaaa"
vector<int> *pointv = new vector<int>{1, 2, 3, 4, 5};
概念 “值初始化”:用()
來初始化
int *pointi = new int();
auto pointi_t = new auto(pointi);
//前面的auto推斷出的是int **
推薦在delete
後將該指針設置成nullptr
C++中出現了智能指針,會幫助程序猿收回內存。
創建新工程,觀察內存泄漏
MFC應用程序能夠在一定程度上(程序推出的時候),幫助發現內存泄漏。
MFC:微軟公司出的基礎的程序框架(MFC基礎類庫),可以生成一個帶窗口(帶界面)的程序框架,MFC簡化了很多界面開發工作。
快捷鍵 ctrl + ]
跳轉到對應的{
或 }
02 new、delete探祕,智能指針概述、shared_ptr基礎
1 new/delete探祕
new/delete是什麼
operator new()和operator delete()
基本new如何記錄分配的內存大小供delete使用
申請和釋放一個數組
爲什麼new/delete、new[]/delete[]要配套使用2 智能指針總述
3shared_ptr
基礎常規初始化(
shared_ptr
和new
配合)
make_shared
函數
new/delete探祕
new/delete是什麼?
new/delete
類對象時,有初始化/釋放的能力(調用構造函數/析構函數),這些是malloc/free
所不能的。
operator new()和operator delete()
new/delete是操作符,operater new()和operator delete()是函數
new
簡單理解爲做了兩件事:a)分配內存(實際上通過operator new()
);b)給內存初始化delete
也做了兩件事:a)反初始化(調用析構函數);b)釋放內存(operator delete()
)
基本new如何記錄分配的內存大小供delete使用
不同編譯器new內部有不同的實現方式,new內部有記錄機制,記錄分配出多少內存。
申請和釋放一個數組
int *p = new int[2];
delete[] p;
如果申請一個類數組,會在內存中存入該數組中類的個數,以便與調用等量的析構函數。
智能指針總述
- 裸指針:直接用
new
返回的指針,強大靈活,但是需要開發者全程維護。 - 智能指針:理解爲“裸指針”進行了包裝,能夠“自動釋放所指向的對象內存”。C++標準庫std提供了四種智能指針:
- 都是類模版
auto_ptr
(C++98)已經被unique_ptr取代unique_ptr
(C++11):獨佔式指針。同一時間內,只有一個指針指向該對象shared_ptr
(C++11):共享式指針。多個指針指向同一個對象,最後一個指針被銷燬時,這個對象會被銷燬weak_ptr
(C++11)用來輔助shared_ptr
- 都是類模版
- 用來幫助我們進行動態分配對象(new出來的對象)的生命週期的管理。能夠有效防止內存泄漏
shared_ptr基礎
常規初始化(shared_ptr和new配合)
每個share_ptr的拷貝都指向相同的內存
工作原理:引用計數。只有最後一個指向該內存的shared_ptr
不需要再指向該對象時,纔會析構這個對象。計數的作用是每增加一個shared_ptr
count + 1,銷燬指針到count爲0時,就會釋放內存。
std::shared_ptr<int> pi(new int(100));
//智能指針explicit不允許隱式轉換
//故std::shared_ptr<int> pi = new int;是錯誤的
make_share函數:標準庫裏的函數模版
在自定義刪除器時會受到限制
shared_ptr<int> pt = make_shared<int>(100);
03 shared_ptr常用操作、計數、自定義刪除器
1 shared_ptr引用計數的增加和減少
引用計數的增加
引用計數的減少2 shared_ptr指針常用操作
use_count()
unique()
reset()
*解應用
get()
swap()
=nullptr
智能指針名字作爲判斷條件
指定刪除器以及數組問題
shared_ptr引用計數的增加和減少
每個shared_ptr
都會記錄有多少個其他的shared_ptr
指向相同的對象。
shared_ptr指針常用操作
use_count()
:返回多少個智能指針指向某個對象,主要用於調試目的unique()
:是否該智能指針獨佔某個指向的對象,如果是,返回Truereset()
:復位/重置- 不帶參數時
- 若該指針是唯一指向該對象的指針,那麼釋放該對象,並將該指針置空(nullptr)
- 若該指針不是唯一指向該對象的指針,那麼不釋放該對象,但計數減一,該指針置空(nullptr)
- 帶參數時(一般是new出來的指針)
- 若該指針是唯一指向該對象的指針,那麼釋放該對象,並將該指針指向新對象
- 若該指針不是唯一指向該對象的指針,那麼不釋放該對象,但計數減一,該指針指向新對象
- 不帶參數時
*解應用
:獲得指針指向的對象get()
:返回保存的指針(裸指針)。用於有些函數的參數需要的是一個內置裸指針而不是智能指針swap()
:交換兩個智能指針所指向的對象=nullptr
- 將所指向的對象引用計數減一,若引用計數變爲0,則釋放該指針指向的對象
- 將智能指針置空
- 指定刪除器以及數組問題
- 可以指定自己的刪除器函數取代系統的默認刪除器
- 管理動態數組時
//定義一個刪除器函數
void deletefunc(int *p)
{
delete []p;
}
shared_ptr<int> pt(new int[100], deletefunc);
//打包
template<typename T>
shared_ptr<T> make_array(size_t n)
{
return shared_ptr<T>(new T[n], default_delete<T[]>());
}
shared_ptr pt = make_array<int>(3); //長度爲3的整形數組,自定義刪除器
//lambda函數做自定義刪除器
shared_ptr<int> pt(new int[10], [](int *p) {
delete []p;
}
//default_delete做刪除器
shared_ptr<int> pt(new int[10], default_delete<int[]>());
//C++17
shared_ptr<int[]> pt(new int[10]); //在<>中加[]
size_t
源碼中 -> typedef unsigned __int64 size_t;
-
32位系統的size_t是4字節的unsigned int
-
64位系統的size_t是8字節的unsigned long long
unsigned int 0~4294967295
int -2147483648~2147483647
unsigned long 0~4294967295
long -2147483648~2147483647
long long的最大值:9223372036854775807
long long的最小值:-9223372036854775808
unsigned long long的最大值:18446744073709551615__int64的最大值:9223372036854775807
__int64的最小值:-9223372036854775808
unsigned __int64的最大值:18446744073709551615
weak_ptr概述、weak_ptr常用操作、尺寸
1 weak_ptr概述
weak_ptr的創建
2 weak_ptr常用操作
use_count()
expired()
reset()
lock()3 尺寸問題
weak_ptr概述
weak_ptr
:用來輔助shared_ptr
進行工作。這個智能指針指向一個由shared_ptr
管理的對象。但是它並不控制所指向對象的生命週期(不會改變shared_ptr
的引用計數)。shared_ptr
能夠照常釋放所指向的對象。
weak_ptr的創建
一般都用shared_ptr
初始化。
auto pi = make_shared<int>(100);
weak_ptr<int> pwi(pi);
//不會改變強引用(strong ref)的引用計數,但是會增加弱引用(weak ref)計數
weak_ptr常用操作
use_count
:獲取當前所觀測資源的強引用計數expired()
:是否過期,用來判斷所觀測資源是否被釋放reset()
:將該弱引用指針設置爲lock()
:功能是檢查weak_ptr
所指向的對象是否存在。如果存在返回一個指向該對象的shared_ptr
,如果不存在則返回一個空的shared_ptr
//接着
auto pi2 = pwi.lock(); //會使強引用加1,pwi仍是弱引用
尺寸問題
weak_ptr
的尺寸和shared_ptr
一樣大,是裸指針的2倍(8字節)。實際上在它們內部包含了兩個指針。
05 shared_ptr使用場景、陷阱、性能分析、使用建議
1
std::shared_ptr
使用場景
2std::shared_ptr
使用陷阱分析
3 性能說明尺寸問題
移動語義
std::shared_ptr使用陷阱分析
- 慎用裸指針
- 當把一個普通裸指針綁定到了一個shared_ptr上之後,內存管理的責任就交給了智能指針,這時就不應該再用裸指針訪問shared_ptr指向的內存了
- 不能用裸指針初始化多個shared_ptr,會導致多個智能指針之間不關聯,刪除器會由於兩次釋放相同內存而報錯
- 慎用get()返回的指針
- get返回的指針不能delete,否則會異常
- 不能將其他智能指針綁到get返回的指針上
- 不要把類對象指針(this)作爲shared_ptr返回,改用
enable_shared_from_this
(工作中需要返回該調用函數本身的類的智能指針時使用)- 工作原理:enable_shared_from_this中又一個弱指針weak_ptr,這個弱指針能夠監視this,調用
shared_from_this
時,實際上調用了這個weak_ptr的lock方法
- 工作原理:enable_shared_from_this中又一個弱指針weak_ptr,這個弱指針能夠監視this,調用
class CT : public enable_shared_from_this<CT>
{
public:
shared_ptr<CT> getself()
{
return shared_from_this(); //這個是enable_shared_from_this類模板中的方法
}
};
性能說明
尺寸問題
shared_ptr
和weak_ptr
均佔8個字節,也就是兩個指針,一個指針指向該指向對象的智能指針;一個指針指向控制塊(包括強引用計數、弱引用計數、刪除器、分配器等)
移動語義
移動構造一個新的智能指針
shared_ptr<int> p1(new int(100));
shared_ptr<int> p2(std::move(p1));
會將p1指向的對象移動給p2,p1不再指向該對象(變成空指針)。
06 unique_ptr概述、常用操作
1 unique_ptr概述
常規初始化
make_unique函數2 unique_ptr常用操作
unique_ptr概述
獨佔式的概念(專屬所有權):同一個時刻只能有一個unique_ptr指向一個對象。當被銷燬時,所指向的對象同時也被銷燬。
unique_ptr<int> pt(new int(103));
//c++14:make_unique
unique_ptr<int> pt = make_unique<int>(104);
auto pt = make_unique<int>(105);
unique_ptr常用操作
- 移動語義
unique_ptr<string> ps1(new string("ingenious"));
unique_ptr<string> ps2(std::move(ps1));
release()
:放棄對指針的控制權,切斷指針和對象之間的聯繫。返回裸指針,並將該智能指針置空,該裸指針需手動釋放
unique_ptr<string> ps1(new string("ingenuous"));
unique_ptr<string> ps2(ps1.release());
reset()
- 不帶參數:釋放智能指針指向的對象,並把指針置空
- 帶參數:釋放智能指針指向的對象,並將智能指針指向括號內的新對象
unique_ptr<string> ps3(new string("fractious"));
ps3.reset(ps1.release());
= nullptr
:釋放指向的對象並置空該智能指針get()
:返回智能指針中保存的裸指針swap()
:交換兩個指針所指向的對象- 轉換成
shared_ptr
形式:如果unique_ptr爲右值就可以轉換成shared_ptr形式
auto func()
{
return unique_ptr<string> st(new string("fd")); //返回的是臨時對象,是右值
}
shared_ptr<string> p = func();
07 返回unique_ptr、刪除器、尺寸、智能指針總結
1 返回unique_ptr
2 指定刪除器
3 尺寸問題
4 智能指針總結
指定刪除器
格式: unique_ptr<指向的對象類型, 刪除器類型> 指針名
void mydelete(string* p)
{
delete p;
p = nullptr;
}
//typedef void (*pr) (string *);
//using pr = void (*) (string *);
typedef decltype(mydelete)* pr;
unique_ptr<string, pr> point(new string("hei"), mydelete);
unique_ptr自定義刪除器需要在模板參數中加入刪除器函數的類型,例中提供了用函數指針做模板參數的情況。
尺寸問題
通常情況下,unique_ptr和裸指針的大小一樣(4字節)。但是自定義刪除器就可能增加所佔用的內存。
總結
- 智能指針的設計目的:幫助釋放內存,以防內存泄漏
- auto_ptr爲什麼被廢棄?
- 不能再容器中保存
- 不能從函數中返回auto_ptr
- C++11提出的unique_ptr相比C++98的auto_ptr更安全
- … …