C++ 11 創建和使用 shared_ptr


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_caststatic_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);
    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM