在現代 C++ 編程中,標準庫包含了智能指針(Smart pointers)。編程
智能指針用來確保程序不會出現內存和資源的泄漏,而且是"異常安全"(exception-safe)的。安全
智能指針定義在頭文件 memory
裏的命名空間 std
中。它對於資源獲取即初始化(RAII, Resource Acquisition Is Initialization) 編程理念相當重要。該理念的目的是保證對象初始化的時候也是資源獲取的時候,從而使對象的全部資源在單行代碼中建立。函數
實踐中,RAII 的主要原則就是把任何在堆上分配的資源(好比動態分配的內存或者系統對象的處理)的全部權提供給在棧上分配的對象(其析構函數包含釋放資源及相關清理的代碼)。工具
大多數時候,當你初始化一個原始指針或者資源句柄使其指向實際的資源時,當即將其傳給智能指針。性能
在現代 C++ 中,原始指針只用於包含在局部做用域,循環或者工具函數的小塊代碼中(對性能有要求,而且對資源的全部權也不容易混淆)。ui
原始指針和智能指針的聲明比較以下:設計
void UseRawPointer() { // Using a raw pointer -- not recommended. Song* pSong = new Song(L"Nothing on You", L"Bruno Mars"); // Use pSong... // Don't forget to delete! delete pSong; } void UseSmartPointer() { // Declare a smart pointer on stack and pass it the raw pointer. unique_ptr<Song> song2(new Song(L"Nothing on You", L"Bruno Mars")); // Use song2... wstring s = song2->duration_; //... } // song2 is deleted automatically here.
如上所示,智能指針是一個在棧上聲明的類模板,並由指向分配在堆上的對象的原始指針初始化。當智能指針初始化後,它就擁有了原始指針的全部權。這意味着智能指針須要負責原始指針指向的內存釋放。智能指針的析構函數包含了 delete
的調用,而且因爲智能指針是在棧上聲明的,其析構函數會在智能指針對象離開做用域時被調用,即便在棧中發生了異常。指針
經過使用指針運算符(->
和 *
)訪問被封裝的指針,智能指針類重載了這些運算符以返回被封裝的原始指針。code
C++ 智能指針的理念相似於在 C# 語言中建立對象的過程:建立對象後讓系統負責在正確的時間將其刪除。不一樣之處在於,沒有獨立的垃圾回收器運行於後臺;內存是按照標準 C++ 規範對內存進行管理的,使運行時環境更加快速和高效。對象
[!重要]
老是在單獨的行上建立智能指針,而不是在參數列表中,從而避免因爲特定的參數列表分配規則出現一些輕微的內存泄漏
如下示例顯示了 C++ 標準庫中的 unique_ptr
是如何封裝指向大型對象的指針的。
class LargeObject { public: void DoSomething(){} }; void ProcessLargeObject(const LargeObject& lo){} void SmartPointerDemo() { // Create the object and pass it to a smart pointer std::unique_ptr<LargeObject> pLarge(new LargeObject()); //Call a method on the object pLarge->DoSomething(); // Pass a reference to a method. ProcessLargeObject(*pLarge); } //pLarge is deleted automatically when function block goes out of scope.
上述示例演示了使用智能指針的關鍵步驟:
new
或者 malloc
表達式)。new
建立的對象的指針傳給智能指針的構造函數。->
和 *
來訪問對象。delete
對象。智能指針在設計上兼顧了內存和性能的高效性。例如,unique_ptr
惟一的數據成員是被封裝的原始指針,這意味着 unique_ptr
具備原始指針一樣地大小,4 字節或者 8 字節。經過智能指針重載的操做符 ->
和 *
來訪問並不比直接使用原始指針來訪問慢多少。
智能指針有其本身的成員函數,經過 .
來訪問。例如,一些 C++ 標準庫的智能指針有用於重置的成員函數來釋放對原始指針的全部權。這能夠用於在智能指針超出做用域前釋放智能指針管理的內存,看下面的示例:
void SmartPointerDemo2() { // Create the object and pass it to a smart pointer std::unique_ptr<LargeObject> pLarge(new LargeObject()); //Call a method on the object pLarge->DoSomething(); // Free the memory before we exit function block. pLarge.reset(); // Do some other work... }
智能指針一般提供了獲取原始指針的方式。 C++ 標準庫中的智能指針包含了成員函數 get
來獲取原始指針。 CComPtr
有公共的類成員 p
。經過獲取原始指針,你可以使用智能指針來管理你本身代碼涉及的內存並依然可以將原始指針傳遞給不支持智能指針的代碼。
void SmartPointerDemo4() { // Create the object and pass it to a smart pointer std::unique_ptr<LargeObject> pLarge(new LargeObject()); //Call a method on the object pLarge->DoSomething(); // Pass raw pointer to a legacy API LegacyLargeObjectFunction(pLarge.get()); }
如下部分總結了在 Windows 環境下不一樣種類的智能指針,以及如何使用它們。
優先使用下列智能指針來封裝原始指針指向的純舊對象(plain old C++ objects,POCO):
unique_ptr
shared_ptr
auto_ptr
,auto_ptr
已做廢boost::scoped_ptr
,unique_ptr
更加小巧和高效shared_ptr
delete
直到全部的 shared_ptr
超出做用域或者放棄全部權。weak_ptr
shared_ptr
使用的特殊智能指針。weak_ptr
提供了對被一個或者多個 shared_ptr
所擁有的對象的訪問,但不參與引用計數。weak_ptr
shared_ptr
實例間的循環引用。