建議使用 Visual Studio 2017

如何:建立和使用 shared_ptr 執行個體

 

如需 Visual Studio 2017 的最新文件請參閱 Visual Studio 2017 文件

shared_ptr 型別是在 C++ 標準程式庫中的一種智慧型指標,是為一個以上的擁有者可能必須管理物件在記憶體中的存留期之情節所設計。 在您初始化shared_ptr 後,您可以複製、 以函式引數的值傳送也可以將它賦值給其他 shared_ptr 執行個體。 所有執行個體都指向相同的物件,並對一「控制區塊」共用存取其參考計數的遞增和遞減,當新的 shared_ptr 加入,將會超出範圍或重設。 在參考計數達到零時,控制區塊會刪除記憶體資源和自己本身。

下圖顯示的是指向一記憶體位置的多個 shared_ptr 執行個體。

共用指標

可能的話,當記憶體資源第一次建立時,請使用 make_shared 函式建立 shared_ptrmake_shared 是例外狀況安全的。 它會使用相同的呼叫去配置控制區塊和資源的記憶體,因而降低建構的額外負荷。 如果您不使用 make_shared,則在將它傳遞給 shared_ptr 建構函式前,您必須先使用明確的新運算式建立物件。 下列範例顯示各種宣告和初始化 shared_ptr 及新物件的方式。


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

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

當您使用複製項目的演算法時,shared_ptr 也適用於 Standard Template Library (STL) 容器。 您可以將 shared_ptr 中的項目包裝,然後將它複製到能夠辨識只要需要且不再需要即有效之基礎記憶體的其他容器中。 下列範例說明如何在向量中的 shared_ptr 執行個體運用 replace_copy_if 演算法。

    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->artist.compare(L"Bob Dylan") == 0;		
    });

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

您可以使用 dynamic_pointer_caststatic_pointer_castconst_pointer_cast 轉換成 shared_ptr。 這些函式類似 dynamic_caststatic_castconst_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 時,判斷被呼叫端是否必須共用基底資源的擁有權。 「Owner」是一個只要需要基礎資源就能使用它的物件或函式。 如果呼叫端必須確保被呼叫端可以擴充指標的存留期在其 (函式) 的存留期之外的,請使用第一個選項。 如果您不在乎被呼叫端是否擴充存留期,則傳參考並讓被呼叫端複製它。

  • 如果您必須允許 Helper 函式對基底指標的存取,並且您知道 Helper 函式將使用指標並在呼叫的函式傳回之前傳回,則此函式不需要共用基底指標的擁有權。 它只能存取在呼叫端的 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; 

智慧型指標

顯示: