智能指針C++11
(讀書筆記:全部摘抄自cPP標註庫)
C++ 11 中,標準庫提供了兩大類smart pointer:
1. Class shared_ptr 實現共享式擁有(shared ownership)概念。多個 只能指針 可以指向相同的對象,改對象和其相關資源會在 “最後一個reference被銷毀” 時釋放。為了在結構較為復雜的情境中執行上述工作,標準庫提供了weak_ptr ,bad_weak_ptr 和 enable_shared_from_this等輔助類。
2,Class_unique_ptr 實現獨占式擁有(exclusive ownership) 或 嚴格擁有(strict ownership)概念。保證同一時間內只有一個smart pointer可以指向該對象。你可以移交擁有權。他對於 避免資源泄露特別有用。
c++98只讓c++標準庫提供一個smart pointer class : auto_ptr<>,其設計是為了執行現今的unique_ptr所提供的服務。然而由於當時缺乏語言特性如“針對構造和賦值”的move語義,以及其他瑕疵,這個class不易被理解且容易出錯。因此在TR1引入clss shared_ptr,c++11引入class unique_ptr之後,auto_ptr成為c++11中被證實反對的成分,除非老舊代碼需要編譯,否則你不應使用它。
所有smart pointer class 都被定義於頭文件<memory>內。
shard_ptr 的目標是沒有蛀牙(在其所指向的對象不再被需要之後,自動釋放與對象相關的資源)。
使用shard_ptr
#include<iostream> #include<string> #include<vector> #include<memory> using namespace std; int main(){ shared_ptr<string> pNico(new string("nico")); shared_ptr<string> pJutta(new string("jutta")); (*pNico)[0] = ‘N‘; pJutta->replace(0,1,"J"); vector<shared_ptr<string>> whoMadeCoffee; whoMadeCoffee.push_back(pJutta); whoMadeCoffee.push_back(pJutta); whoMadeCoffee.push_back(pNico); whoMadeCoffee.push_back(pNico); for(auto ptr : whoMadeCoffee) { cout << *ptr << " "; } cout << endl; *pNico = " Nicolai"; for(auto ptr : whoMadeCoffee) { cout << *ptr << " "; } cout << endl; cout << "use_cout " << whoMadeCoffee[0].use_count() << endl; }
需要註意的是,由於“接受單一pointer作為唯一實參" 的構造函數是explicit,所以這裏不能使用賦值符,因為那樣的話會被視為需要一個隱式轉換,然而新式的語法是被接受的:
shared_ptr<string> pNico = new string("nico"); //ERROR shared_ptr<string> pNico{new string("nico")}; //OK
也可以使用便捷的 make_shared() :
shared_ptr<string> pNico = make_shared<string>("nico");
這種方式比較快,也比較安全,因為它使用一次而非二次分配:一次針對對象,另一次針對"shared pointer 用以控制對象" 的shared data。
另一種寫法是,先聲明shard pointer ,然後對它賦值一個new pointer,然後不可以使用assignment操作符,必須改用reset():
pNico3 = new string("nico"); //ERROR :no assignment for ordinary pointers pNico3.reset(new string("nico")); //Ok
定義一個Deleter
定義一個deleter,例如讓它在“刪除被指向對象”之前先打印一條信息:
shared_ptr<string> pNico(new string("nico"), [](string * p){ cout << "delete " << *p << endl; delete p; }); pNico = nullptr; //pNico does not refer to string any longer whoMadeCoffee.resize(2); // all copies of string in pNico are destoryed
其中函數對象 D del參數是一個lambda表達式。
關於shared_ptr 的構造函數,參考鏈接
對付Array
shared_ptr提供的default deleter 調用的是delete,不是delete[] ,這意味著局限性,只有當shared_ptr擁有“由new建立起來的單一對象”,default deleter才試用。然而很不幸,為array建立一個shared_ptr是可能的,卻是錯誤的:
std::shared_ptr<int> p(new int[10]); //ERROR,but compiles
故,如果你試用new[]建立一個array of object ,必須自己定義自己deleter。你可以傳遞一個函數,或者函數對象,或者lambda,讓它們針對傳入的尋常指針調用delete[]。例如:
shared_ptr<int> p(new int[10], [](int *p) { delete[] p; });
也可以試用為unique_ptr而提供的輔助函數作為deleter,其內調用deleter[]:
std::shared_ptr<int> p(new int[10], std::default_delete<int[]> ());
需要註意的是:shared_ptr 和unique_ptr 以稍稍不同的方式處理deleter。例如unique_ptr允許只傳遞對應的元素類型作為template實參,但這對shared_ptr就不行:
std::unique_ptr<int[]> p(new int[10]); //OK std::shared_ptr<int[]> p(new int[10]); //ERROR:does not compile
此外,對於 unique_ptr,你必須明確給予第二個template實參,指出自己的deleter:
std::unique_ptr<int,void(*)(int *)> p(new int[10], [](int *p) { delete[] p; });
還需註意,shared_ptr不提供operator[]。至於unique_ptr,它有一個針對array的偏特化版本,該版本提供operator[] 取代 operator* 和 operator-> 。之所以有此差異是因為,unique_ptr在效能和彈性上進行了優化。
智能指針C++11