shared_ptr 的類型是C + +標准庫中一個聰明的指針,是為多個擁有者管理內存中對象的生命周期而設計的。在你初始化一個 shared_ptr 后,你可以復制它,把函數參數的值遞給它,並把它分配給其它 shared_ptr 實例。所有實例指向同一個對象,並共享訪問一個“控制塊”,即每當一個新的shared_ptr 被添加時,遞增和遞減引用計數,超出范圍,則復位。當引用計數到達零時,控制塊刪除內存資源和自身。
下圖顯示了指向一個內存位置的幾個 shared_ptr 實例。
無論什么時候,當內存資源被第一次被創建時,就使用函數 make_shared (<memory>) 創建一個新的 shared_ptr。 make_shared異常安全。它使用同一調用分配的內存控制塊和資源從而減少構造開銷。如果你不使用 make_shared,那么在把它傳遞給 shared_ptr 的構造函數之前,你必須使用一個明確的新表達式創建的對象。下面的例子顯示了在新對象中聲明和初始化一個 shared_ptr 的各種方式。
class Song { public: Song(std::string str1, std::string str2); ~Song(); private: std::string name; std::string action; };
// Use make_shared function when possible. auto sp1 = make_shared<Song>(L"The Beatles", L"Im Happy Just to Dance With You"); // Ok, but slightly less efficient. // Note: Using new expression as constructor argument // creates no named variable for other code to access. shared_ptr<Song> sp2(new Song(L"Lady Gaga", L"Just Dance")); // When initialization must be separate from declaration, e.g. class members, // initialize with nullptr to make your programming intent explicit. shared_ptr<Song> sp5(nullptr); //Equivalent to: shared_ptr<Song> sp5; //... sp5 = make_shared<Song>(L"Elton John", L"I'm Still Standing");
下面的示例演示如何聲明和初始化一個已經被分配了另一個 shared_ptr 的對象共享所有權的 shared_ptr 的實例。假設 sp2 是一個初始化的shared_ptr。
//Initialize with copy constructor. Increments ref count. auto sp3(sp2); //Initialize via assignment. Increments ref count. auto sp4 = sp2; //Initialize with nullptr. sp7 is empty. shared_ptr<Song> sp7(nullptr); //此指針有指向類型,但是指向nullptr // Initialize with another shared_ptr. sp1 and sp2 // swap pointers as well as ref counts. sp1.swap(sp2);
當您使用算法復制元素時,shared_ptr 的也是很有用的標准模板庫(STL)。你可以把元素包裝在 shared_ptr 里,然后將其復制到其他容器,只要你需要它,底層的內存始終是有效的。以下示例演示如何使用 replace_copy_if 算法來創建一個 shared_ptr 的實例以及如何在一個向量上進行使用。
vector<shared_ptr<Song>> v; v.push_back(make_shared<Song>(L"Bob Dylan", L"The Times They Are A Changing")); v.push_back(make_shared<Song>(L"Aretha Franklin", L"Bridge Over Troubled Water")); v.push_back(make_shared<Song>(L"Thalxa", L"Entre El Mar y Una Estrella")); vector<shared_ptr<Song>> v2; remove_copy_if(v.begin(), v.end(), back_inserter(v2), [] (shared_ptr<Song> s) { return s->artist.compare(L"Bob Dylan") == 0; }); for (const auto& s : v2) { wcout << s->artist << L":" << s->title << endl; }
你可以用 dynamic_pointer_cast, static_pointer_cast 和 const_pointer_cast 來轉換shared_ptr。這些函數的操作類似 dynamic_cast,static_cast 和 const_cast。下面的示例演示如何測試在基類的 shared_ptr 向量中的每個元素的派生類,,然后復制元素,並顯示它們的信息。
vector<shared_ptr<MediaAsset>> assets; assets.push_back(shared_ptr<Song>(new Song(L"Himesh Reshammiya", L"Tera Surroor"))); assets.push_back(shared_ptr<Song>(new Song(L"Penaz Masani", L"Tu Dil De De"))); assets.push_back(shared_ptr<Photo>(new Photo(L"2011-04-06", L"Redmond, WA", L"Soccer field at Microsoft."))); vector<shared_ptr<MediaAsset>> photos; copy_if(assets.begin(), assets.end(), back_inserter(photos), [] (shared_ptr<MediaAsset> p) -> bool { // Use dynamic_pointer_cast to test whether // element is a shared_ptr<Photo>. shared_ptr<Photo> temp = dynamic_pointer_cast<Photo>(p); return temp.get() != nullptr; }); for (const auto& p : photos) { // We know that the photos vector contains only // shared_ptr<Photo> objects, so use static_cast. wcout << "Photo location: " << (static_pointer_cast<Photo>(p))->location_ << endl; }
你可以用下列方法把 shared_ptr 傳遞給另一個函數:
-
向 shared_ptr 傳遞值。調用復制構造函數,遞增引用計數,並把被調用方當做所有者。還有就是在這次操作中有少量的開銷,這很大程度上取決於你傳遞了多少 shared_ptr 對象。當調用方和被調用方之間的代碼協定 (隱式或顯式) 要求被調用方是所有者,使用此選項。
-
通過引用或常量引用來傳遞 shared_ptr。在這種情況下,引用計數不增加,並且只要調用方不超出范圍,被調用方就可以訪問指針。或者,被調用方可以決定創建一個基於引用的 shared_ptr,從而成為一個共享所有者。當調用者並不知道被被調用方,或當您必須傳遞一個 shared_ptr,並希望避免由於性能原因的復制操作,請使用此選項。
-
通過底層的指針或引用底層的對象。這使得被調用方使用對象,但不使共享所有權或擴展生存期。如果被調用方從原始指針創建一個shared_ptr,則新的 shared_ptr 是獨立於原來的,且沒有控制底層的資源。當調用方和被調用方之間的協定中明確規定調用者保留shared_ptr 生存期的所有權,則使用此選項。
-
當您決定如何傳遞一個 shared_ptr時,確定被調用方是否有共享基礎資源的所有權。一個“所有者”就是只要它需要就可以使用底層資源的對象或函數。如果調用方必須保證被調用方可以在其(函數)生存期以外擴展指針的生存期,請使用第一個選項。如果您不關心被調用方是否擴展生存期,則通過引用傳遞並讓被調用方復制它。
-
如果不得不允許幫助程序函數訪問底層指針,並且您知道幫助程序函數將使用指針且在調用函數返回前先返回,則該函數不必共享底層指針的所有權。僅僅是在調用方的 shared_ptr 的生存期內允許訪問指針。在這種情況下,通過引用來傳遞 shared_ptr,通過原始指針或引用的基本對象都是安全的。通過此方式提供一個小的性能改進,並且還有助於表示程序的意圖。
-
有時,例如在一個 std:vector<shared_ptr<T>>中,您可能必須對傳遞每個 shared_ptr 給lambda表達式體或命名函數對象。如果lambda或函數沒有存儲指針,則通過引用傳遞 shared_ptr,以避免調用拷貝構造函數的每個元素。
下面的示例顯示 shared_ptr 如何重載多種比較操作符,以使由 shared_ptr 實例所擁有的內存指針的比較。
// Initialize two separate raw pointers. // Note that they contain the same values. auto song1 = new Song(L"Village People", L"YMCA"); auto song2 = new Song(L"Village People", L"YMCA"); // Create two unrelated shared_ptrs. shared_ptr<Song> p1(song1); shared_ptr<Song> p2(song2); // Unrelated shared_ptrs are never equal. wcout << "p1 < p2 = " << std::boolalpha << (p1 < p2) << endl; wcout << "p1 == p2 = " << std::boolalpha <<(p1 == p2) << endl; // Related shared_ptr instances are always equal. shared_ptr<Song> p3(p2); wcout << "p3 == p2 = " << std::boolalpha << (p3 == p2) << endl;
舉例:
{ //創建的空指針,有指針類型,但是沒有指向對象 std::shared_ptr<int> fPtr1; std::shared_ptr<int> fPtr2 = std::make_shared<int>(4); std::cout << "fPtr2 use_count:" << fPtr2.use_count() << std::endl; std::shared_ptr<int> fPtr3(fPtr2); std::cout << "fPtr2 use_count:" << fPtr2.use_count() << " fPtr3 use_count:" << fPtr3.use_count() << std::endl; std::shared_ptr<int> fPtr4 = std::make_shared<int>(10); //shared_ptr指針對象賦值可直接用= std::shared_ptr<int> fPtr5 = fPtr2; std::cout << "fPtr2 use_count:" << fPtr2.use_count() << " fPtr3 use_count:" << fPtr3.use_count() << std::endl; std::cout << "-------------------------------------------" << std::endl; //get() 返回指向對象的指針 std::cout << "fPtr2 address:" << fPtr2.get() << " = fPtr3 address:" << fPtr3.get() << std::endl; //use_count 返回指針指向的對象的引用計數 std::cout << "fPtr2 use_count:" << fPtr2.use_count() << " fPtr3 use_count:" << fPtr3.use_count() << std::endl; if (!fPtr1) { std::cout << "fPtr is nullptr" << std::endl; } std::cout << "-------------------------------------------" << std::endl; //swap 將還兩個shared_ptr所指向的對象 std::cout << "swap before fPtr2 = " << *fPtr2 << " fPtr4 = " << *fPtr4 << std::endl; fPtr4.swap(fPtr2); std::cout << "swap after fPtr2 = " << *fPtr2 << " fPtr4 = " << *fPtr4 << std::endl; //交換之后,引用計數也跟着變 std::cout << "fPtr2 use_count:" << fPtr2.use_count() << " fPtr4 use_count:" << fPtr4.use_count() << std::endl; //返回shared_ptr指針指向的對象引用是否為1 std::cout << "unique:" << std::boolalpha << fPtr1.unique() << std::endl; //reset 將當前shared指針計數設置為0,並將該指針所引用的對象計數減1,刷新所有引用此對象的shared指針 std::cout << "before reset fPtr2 use_count = " << fPtr2.use_count() << " fPtr3 = " << fPtr3.use_count() << std::endl; fPtr3.reset(); std::cout << "after reset fPtr2 use_count = " << fPtr2.use_count() << " fPtr3 = " << fPtr3.use_count() << std::endl; std::cout << "-------------------------------------------" << std::endl; std::vector<std::tr1::shared_ptr<int> > numbers; numbers.push_back(std::tr1::shared_ptr<int>(new int(1))); numbers.push_back(std::tr1::shared_ptr<int>(new int(2))); numbers.push_back(std::tr1::shared_ptr<int>(new int(3))); //如果聲明std::tr1::shared_ptr<const 類型>,而當前需要修改對象的值, //可以聲明std::tr1::shared_ptr<int> sp = std::tr1::const_pointer_cast<int>(csp); }