HOW TO:建立及使用 shared_ptr 執行個體How to: Create and Use shared_ptr Instances

shared_ptr 類型是 C++ 標準程式庫中的一種智慧型指標,是為有一個以上的擁有者可能必須管理物件在記憶體中的存留期之情節而設計。The shared_ptr type is a smart pointer in the C++ standard library that is designed for scenarios in which more than one owner might have to manage the lifetime of the object in memory. 在您初始化 shared_ptr 之後,您可以函式引數中的值予以複製、傳送以及指派至其他 shared_ptr 執行個體。After you initialize a shared_ptr you can copy it, pass it by value in function arguments, and assign it to other shared_ptr instances. 所有執行個體都會指向相同的物件,並共用對一個每當新的 shared_ptr 加入、超出範圍或重設時會遞增和遞減參考計數的「控制區塊」的存取。All the instances point to the same object, and share access to one "control block" that increments and decrements the reference count whenever a new shared_ptr is added, goes out of scope, or is reset. 當參考計數達到零時,控制區塊會刪除記憶體資源和自己本身。When the reference count reaches zero, the control block deletes the memory resource and itself.

下圖顯示幾個指向一個記憶體位置的 shared_ptr 執行個體。The following illustration shows several shared_ptr instances that point to one memory location.

共用指標圖表Shared pointer diagram

範例設定Example setup

以下範例假設您已包含必要的標頭,並宣告了必要類型,如此處所示:The examples that follow all assume that you've included the required headers and declared the required types, as shown here:

// shared_ptr-examples.cpp
// The following examples assume these declarations:
#include <algorithm>
#include <iostream>
#include <memory>
#include <string>
#include <vector>

struct MediaAsset
    virtual ~MediaAsset() = default; // make it polymorphic

struct Song : public MediaAsset
    std::wstring artist;
    std::wstring title;
    Song(const std::wstring& artist_, const std::wstring& title_) :
        artist{ artist_ }, title{ title_ } {}

struct Photo : public MediaAsset
    std::wstring date;
    std::wstring location;
    std::wstring subject;
        const std::wstring& date_,
        const std::wstring& location_,
        const std::wstring& subject_) :
        date{ date_ }, location{ location_ }, subject{ subject_ } {}

using namespace std;

int main()
    // The examples go here, in order:
    // Example 1
    // Example 2
    // Example 3
    // Example 4
    // Example 6

範例 1Example 1

在任何可能的情況下,請在初次建立記憶體資源時使用 make_shared 函式來建立 shared_ptrWhenever possible, use the make_shared function to create a shared_ptr when the memory resource is created for the first time. make_shared 是無例外狀況之虞。make_shared is exception-safe. 它會使用相同的呼叫來配置控制區塊的記憶體及資源,減少建構的額外負荷。It uses the same call to allocate the memory for the control block and the resource, which reduces the construction overhead. 若您不使用 make_shared,便必須使用明確的 new 運算式來建立物件,才能將物件傳遞至 shared_ptr 建構函式。If you don't use make_shared, then you have to use an explicit new expression to create the object before you pass it to the shared_ptr constructor. 下列範例顯示各種宣告和初始化 shared_ptr 及新物件的方式。The following example shows various ways to declare and initialize a shared_ptr together with a new object.

// 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");

範例 2Example 2

下列範例顯示如何宣告和初始化 shared_ptr 執行個體,其具有已被另一個 shared_ptr 配置之物件的共用擁有權。The following example shows how to declare and initialize shared_ptr instances that take on shared ownership of an object that has already been allocated by another shared_ptr. 假設 sp2 是已初始化的 shared_ptrAssume that sp2 is an initialized 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);

// Initialize with another shared_ptr. sp1 and sp2
// swap pointers as well as ref counts.

範例 3Example 3

當您在 C++ 標準程式庫容器內使用會複製元素的演算法時,shared_ptr 也相當實用。shared_ptr is also helpful in C++ Standard Library containers when you're using algorithms that copy elements. 您可以包裝 shared_ptr 中的項目,然後將它複製到能夠辨識只有需要時才有效 (不再需要時則無效) 之基礎記憶體的其他容器中。You can wrap elements in a shared_ptr, and then copy it into other containers with the understanding that the underlying memory is valid as long as you need it, and no longer. 下列範例顯示如何在向量中的 remove_copy_if 執行個體上運用 shared_ptr 演算法。The following example shows how to use the remove_copy_if algorithm on shared_ptr instances in a vector.

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"Thal�a", 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->"Bob Dylan") == 0;		

for (const auto& s : v2)
    wcout << s->artist << L":" << s->title << endl;

範例 4Example 4

您可以使用 dynamic_pointer_caststatic_pointer_castconst_pointer_cast 轉換 shared_ptrYou can use dynamic_pointer_cast, static_pointer_cast, and const_pointer_cast to cast a shared_ptr. 這些函式類似 dynamic_caststatic_castconst_cast 運算子。These functions resemble the dynamic_cast, static_cast, and const_cast operators. 下列範例顯示如何測試在基底類別的 shared_ptr 向量中每個項目的衍生類型,然後複製項目並顯示其相關資訊。The following example shows how to test the derived type of each element in a vector of shared_ptr of base classes, and then copy the elements and display information about them.

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;

範例 5Example 5

您可以透過下列方式將 shared_ptr 傳遞至另一個函式:You can pass a shared_ptr to another function in the following ways:

  • 以傳值方式傳遞 shared_ptrPass the shared_ptr by value. 這會叫用複製建構函式、遞增參考計數以及讓被呼叫端成為擁有者。This invokes the copy constructor, increments the reference count, and makes the callee an owner. 此作業中有少量的額外負荷,也可能視您傳遞的 shared_ptr 物件多寡而變多。There's a small amount of overhead in this operation, which may be significant depending on how many shared_ptr objects you're passing. 請在呼叫者和被呼叫者之間的隱含或明確程式碼合約要求被呼叫者成為擁有者時,使用此選項。Use this option when the implied or explicit code contract between the caller and callee requires that the callee be an owner.

  • 以傳址或 const 的傳址方式傳遞 shared_ptrPass the shared_ptr by reference or const reference. 在這種情況下,參考計數不會遞增,並且只要呼叫者沒有離開範圍,被呼叫者都能存取該指標。In this case, the reference count isn't incremented, and the callee can access the pointer as long as the caller doesn't go out of scope. 或者,被呼叫者可以決定根據參考建立一個 shared_ptr,並成為共用擁有者。Or, the callee can decide to create a shared_ptr based on the reference, and become a shared owner. 當呼叫端不了解被呼叫端或者您必須傳遞 shared_ptr 且要基於效能考量避免複製作業時,請使用這個選項。Use this option when the caller has no knowledge of the callee, or when you must pass a shared_ptr and want to avoid the copy operation for performance reasons.

  • 將基底指標或參考傳遞至基礎物件。Pass the underlying pointer or a reference to the underlying object. 這可讓被呼叫者使用物件,但不會讓它共用擁有權或延長存留期。This enables the callee to use the object, but doesn't enable it to share ownership or extend the lifetime. 若被呼叫者從原始指標建立 shared_ptr,則新的 shared_ptr 會獨立於原始的 shared_ptr,並且不會控制基礎資源。If the callee creates a shared_ptr from the raw pointer, the new shared_ptr is independent from the original, and doesn't control the underlying resource. 當呼叫端和被呼叫端之間的協定明確指定呼叫端保留 shared_ptr 存留期的擁有權時,請使用這個選項。Use this option when the contract between the caller and callee clearly specifies that the caller retains ownership of the shared_ptr lifetime.

  • 在您決定如何傳遞 shared_ptr 時,請判斷被呼叫者是否必須共用基礎資源的擁有權。When you're deciding how to pass a shared_ptr, determine whether the callee has to share ownership of the underlying resource. 「擁有者」是一個只要需要時就讓基礎資源存活的物件或函式。An "owner" is an object or function that can keep the underlying resource alive for as long as it needs it. 如果呼叫端必須確保被呼叫端可以延長指標的壽命為超過其 (函式的) 存留期,請使用第一個選項。If the caller has to guarantee that the callee can extend the life of the pointer beyond its (the function's) lifetime, use the first option. 如果您不在乎被呼叫端是否延長存留期,則以傳址方式傳遞,並讓被呼叫端決定是否複製。If you don't care whether the callee extends the lifetime, then pass by reference and let the callee copy it or not.

  • 若您必須讓 helper 函式存取基礎指標,並且您知道 helper 函式只會在呼叫函式傳回之前使用指標並傳回,則該函式便不需要共用基礎指標的擁有權。If you have to give a helper function access to the underlying pointer, and you know that the helper function will just use the pointer and return before the calling function returns, then that function doesn't have to share ownership of the underlying pointer. 它只需要存取呼叫端之 shared_ptr 的存留期內的指標。It just has to access the pointer within the lifetime of the caller's shared_ptr. 在這種情況下,以傳址方式傳遞 shared_ptr,或傳遞基礎物件的原始指標或參考是安全的。In this case, it's safe to pass the shared_ptr by reference, or pass the raw pointer or a reference to the underlying object. 以這種方式傳遞有一小小的效能優點,並且也可以協助您表達程式設計的意圖。Passing this way provides a small performance benefit, and may also help you express your programming intent.

  • 在某些情況下,例如在 std::vector<shared_ptr<T>> 中,您可能必須將每個 shared_ptr 傳遞到 Lambda 運算式主體或具名函式物件。Sometimes, for example in a std::vector<shared_ptr<T>>, you may have to pass each shared_ptr to a lambda expression body or named function object. 若 lambda 或函式並未儲存指標,請以傳址方式傳遞 shared_ptr 來避免叫用每個元素的複製建構函式。If the lambda or function doesn't store the pointer, then pass the shared_ptr by reference to avoid invoking the copy constructor for each element.

範例 6Example 6

以下範例顯示 shared_ptr 如何多載各種比較運算子,以啟用 shared_ptr 執行個體所擁有之記憶體的指標比較。The following example shows how shared_ptr overloads various comparison operators to enable pointer comparisons on the memory that is owned by the shared_ptr instances.

// 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; 

另請參閱See also

智慧型指標 (現代 C++)Smart Pointers (Modern C++)