c++ - smart - unique_ptr delete




什麼是智能指針,我應該什麼時候使用它? (10)

什麼是智能指針,我應該什麼時候使用它?


Chris,Sergdev和Llyod提供的定義是正確的。 我更喜歡簡單的定義,只是為了讓我的生活變得簡單:一個智能指針只是一個重載->*運算符的類。 這意味著你的對像在語義上看起來像一個指針,但是你可以讓它做更酷的事情,包括引用計數,自動銷毀等等shared_ptrauto_ptr在大多數情況下是足夠的,但是伴隨著它們自己的一些小特性。


http://en.wikipedia.org/wiki/Smart_pointer

在計算機科學中,智能指針是一種抽像數據類型,可以在提供其他功能(如自動垃圾收集或邊界檢查)的同時模擬指針。 這些附加功能旨在減少由於濫用指針而導致的錯誤,同時保持效率。 為了內存管理的目的,智能指針通常會跟踪指向它們的對象。 指針的濫用是bug的主要來源:必須由使用指針編寫的程序執行的常量分配,解除分配和引用使得很可能發生一些內存洩漏。 智能指針通過自動進行資源釋放來防止內存洩漏:當指向對象的指針(或指針系列中的最後一個指針)被銷毀時,例如因為它超出了作用域,被指向的對像也被銷毀。


一個智能指針就像一個常規(類型)指針,就像“char *”一樣,除非指針本身超出範圍,那麼它指向的內容也會被刪除。 您可以像使用常規指針那樣使用它,方法是使用“ - >”,但如果您需要實際指向數據的指針,則不會使用它。 為此,您可以使用“&* ptr”。

它對於:

  • 必須用新分配的對象,但您希望與該堆棧中的某些對象具有相同的生命週期。 如果對像被分配給智能指針,那麼當程序退出該功能/塊時,它們將被刪除。

  • 類的數據成員,這樣當對像被刪除時,所有擁有的數據也被刪除,析構函數中沒有任何特殊的代碼(你需要確保析構函數是虛擬的,這幾乎總是一件好事) 。

在下列情況下您可能不想使用智能指針:

  • ......指針實際上不應該擁有數據......也就是說,當你使用數據時,你希望它能夠在你引用它的函數中存活下來。
  • ......智能指針本身並不會在某個時候被破壞。 您不希望它位於永不被破壞的內存中(例如在動態分配的對像中,但不會被明確刪除)。
  • ...兩個智能指針可能指向相同的數據。 (然而,有更聰明的指針會處理這個......這就是所謂的引用計數 。)

也可以看看:


下面是現代C ++這些日子的簡單答案:

  • 什麼是智能指針?
    它是一種可以像指針一樣使用的值,但提供了自動內存管理的附加功能:當指針不再使用時,它指向的內存被釋放(另請參閱維基百科上更詳細的定義 )。
  • 我應該什麼時候使用一個?
    在代碼中涉及跟踪一塊內存的所有權,分配或取消分配; 智能指針通常可以幫助您明確地完成這些任務。
  • 但是,我應該在哪些情況下使用哪個智能指針?
    • 如果您不打算持有對同一對象的多個引用,請使用std::unique_ptr 。 例如,將它用於指向內存的指針,該內存在進入某個範圍時得到分配,並在退出範圍時取消分配。
    • 當你想從多個地方引用你的對象時,使用std::shared_ptr - 並且不希望它被解除分配,直到所有這些引用本身消失。
    • 當你想從多個地方引用你的對象時,使用std::weak_ptr - 對於那些可以忽略和釋放的引用(所以他們只會注意到當你嘗試解引用時對像已經消失)。
    • 不要使用boost:: smart指針或std::auto_ptr除非在特殊情況下,如果必須的話,您可以閱讀它們。
  • 嘿,我沒有問過要用哪一個!
    啊,但你真的想承認這一點。
  • 那麼我應該什麼時候使用常規指針呢?
    主要是在對內存所有權不知情的代碼中。 這通常是在從其他位置獲得指針的函數中,並且不分配,取消分配或存儲超出其執行的指針的副本。

大多數智能指針為你處理指針對象。 這非常方便,因為您不必考慮手動處理對象。

最常用的智能指針是std::tr1::shared_ptr (或boost::shared_ptr ),並且不常用的是std::auto_ptr 。 我建議定期使用shared_ptr

shared_ptr功能非常強大,可以處理各種各樣的處置場景,包括需要“跨DLL邊界傳遞”對象的情況(如果在代碼和DLL之間使用不同的libc則是常見的惡夢案例)。


我想再補充一點,上面的問題,智能指針std :: shared_ptr沒有下標運算符,也不支持pterter運算,我們可以使用get()來獲取內置的指針。


智能指針是一個包裝“裸”(或“裸”)C ++指針的類,用於管理指向的對象的​​生命週期。 沒有單一的智能指針類型,但他們都嘗試以實用的方式提取原始指針。

智能指針應該優於原始指針。 如果你覺得你需要使用指針(首先考慮你是否真的這麼做),你通常會使用智能指針,因為這可以減輕原指針的許多問題,主要是忘記刪除對象和洩漏內存。

使用原始指針時,程序員必須在不再有用時明確銷毀對象。

// Need to create the object to achieve some goal
MyObject* ptr = new MyObject(); 
ptr->DoSomething(); // Use the object in some way
delete ptr; // Destroy the object. Done with it.
// Wait, what if DoSomething() raises an exception...?

通過比較,智能指針定義了一個關於何時銷毀對象的策略。 你仍然需要創建這個對象,但是你不必擔心它會被銷毀。

SomeSmartPtr<MyObject> ptr(new MyObject());
ptr->DoSomething(); // Use the object in some way.

// Destruction of the object happens, depending 
// on the policy the smart pointer class uses.

// Destruction would happen even if DoSomething() 
// raises an exception

使用中最簡單的策略涉及智能指針包裝器對象的範圍,例如由boost::scoped_ptrstd::unique_ptr

void f()
{
    {
       boost::scoped_ptr<MyObject> ptr(new MyObject());
       ptr->DoSomethingUseful();
    } // boost::scopted_ptr goes out of scope -- 
      // the MyObject is automatically destroyed.

    // ptr->Oops(); // Compile error: "ptr" not defined
                    // since it is no longer in scope.
}

請注意,不能複制scoped_ptr實例。 這可以防止多次刪除指針(不正確)。 但是,您可以將引用傳遞給您調用的其他函數。

當您想要將對象的生命週期與特定的代碼塊關聯起來時,或者將其作為成員數據嵌入到另一個對像中時,該對象的生命週期是有用的。 該對像一直存在,直到包含代碼塊被退出,或者直到包含對象本身被銷毀。

更複雜的智能指針策略涉及對指針進行引用計數。 這確實允許指針被複製。 當對象的最後一個“引用”被銷毀時,對像被刪除。 這個策略由boost::shared_ptrstd::shared_ptr

void f()
{
    typedef std::shared_ptr<MyObject> MyObjectPtr; // nice short alias
    MyObjectPtr p1; // Empty

    {
        MyObjectPtr p2(new MyObject());
        // There is now one "reference" to the created object
        p1 = p2; // Copy the pointer.
        // There are now two references to the object.
    } // p2 is destroyed, leaving one reference to the object.
} // p1 is destroyed, leaving a reference count of zero. 
  // The object is deleted.

當對象的生命週期更複雜時,引用計數指針非常有用,並且不直接與特定的代碼段或其他對象綁定。

引用計數指針有一個缺點 - 創建懸掛引用的可能性:

// Create the smart pointer on the heap
MyObjectPtr* pp = new MyObjectPtr(new MyObject())
// Hmm, we forgot to destroy the smart pointer,
// because of that, the object is never destroyed!

另一種可能性是創建循環引用:

struct Owner {
   boost::shared_ptr<Owner> other;
};

boost::shared_ptr<Owner> p1 (new Owner());
boost::shared_ptr<Owner> p2 (new Owner());
p1->other = p2; // p1 references p2
p2->other = p1; // p2 references p1

// Oops, the reference count of of p1 and p2 never goes to zero!
// The objects are never destroyed!

為了解決這個問題,Boost和C ++ 11都定義了一個weak_ptr來定義一個對shared_ptr的弱(不計數)引用。

UPDATE

這個答案相當古老,因此描述了當時的“好”,這是Boost庫提供的智能指針。 自C ++ 11以來,標準庫提供了足夠的智能指針類型,因此您應該傾向於使用std::unique_ptrstd::shared_ptrstd::weak_ptr

還有std::auto_ptr 。 它非常像一個範圍指針,除了它還具有被複製的“特殊”危險能力 - 這也意外地轉移了所有權! 它在最新的標準中被棄用,所以你不應該使用它。 改為使用std::unique_ptr

std::auto_ptr<MyObject> p1 (new MyObject());
std::auto_ptr<MyObject> p2 = p1; // Copy and transfer ownership. 
                                 // p1 gets set to empty!
p2->DoSomething(); // Works.
p1->DoSomething(); // Oh oh. Hopefully raises some NULL pointer exception.

智能指針是一個類,一個普通指針的包裝。 與普通指針不同,智能點的生命週期基於引用計數(指定智能指針對象的次數)。 因此,無論何時將智能指針分配給另一個智能指針,內部引用計數加上加。 每當對象超出範圍時,引用計數減去負數。

自動指針雖然看起來相似,但與智能指針完全不同。 每當自動指針對象超出變量作用域時,便可以輕鬆地釋放資源。 在某種程度上,它使得一個指針(對於動態分配的內存)與堆棧變量類似(在編譯時間內靜態分配)。


現有的答案是好的,但不包括當智能指針不是你正試圖解決的問題的(完整)答案時要做什麼。

除其他事項外(在其他答案中很好地解釋)使用智能指針是一種可能的解決方案我們如何使用抽像類作為函數返回類型? 這已被標記為這個問題的重複。 然而,如果試圖在C ++中指定一個抽象的(或者實際上是任何)基類作為返回類型,第一個問題就是“你究竟是什麼意思?”。 在boost指針容器庫的文檔中,有一個關於C ++中慣用的面向對象編程(以及如何與其他語言不同)的討論(進一步參考)。 總之,在C ++中你必須考慮所有權。 哪些智能指針可以幫助你,但不是唯一的解決方案,或者總是一個完整的解決方案(它們不會給你多態拷貝),並不總是你想在你的界面中公開的解決方案(並且函數返回聽起來很糟糕很像一個界面)。 例如,返回參考可能就足夠了。 但是,在所有這些情況下(智能指針,指針容器或簡單地返回引用),您已將從一個更改為某種形式的引用 。 如果你真的需要復制,你可能需要添加更多的樣板“成語”,或者超越C ++中的慣用(或其他)OOP,使用類似Adobe PolyBoost.TypeErasure等庫的更一般的多態。


設T是本教程中的一個類C ++中的指針可以分為3種類型:

1) 原始指針

T a;  
T * _ptr = &a; 

他們將內存地址保存到內存中的某個位置。 謹慎使用,因為程序變得複雜難以追踪。

使用常量數據或地址的指針{向後讀取}

T a ; 
const T * ptr1 = &a ; 
T const * ptr1 = &a ;

指向一個常量的數據類型T的指針。 這意味著您不能使用指針更改數據類型。 即*ptr1 = 19 ; 不管用。 但是你可以移動指針。 即ptr1++ , ptr1-- ; 等將工作。 向後讀:指向類型T的指針,它是const

  T * const ptr2 ;

一個指向數據類型T的const指針。 意思是你不能移動指針,但你可以改變指針指向的值。 即*ptr2 = 19將工作,但ptr2++ ; ptr2-- ptr2++ ; ptr2--等不起作用。 向後讀:const指針指向類型T

const T * const ptr3 ; 

指向常量數據類型T的const指針。 這意味著您不能移動指針,也不能將數據類型指針更改為指針。 即。 ptr3-- ; ptr3++ ; *ptr3 = 19; 不管用

3) 智能指針 :{ #include <memory> }

共享指針

  T a ; 
     //shared_ptr<T> shptr(new T) ; not recommended but works 
     shared_ptr<T> shptr = make_shared<T>(); // faster + exception safe

     std::cout << shptr.use_count() ; // 1 //  gives the number of " 
things " pointing to it. 
     T * temp = shptr.get(); // gives a pointer to object

     // shared_pointer used like a regular pointer to call member functions
      shptr->memFn();
     (*shptr).memFn(); 

    //
     shptr.reset() ; // frees the object pointed to be the ptr 
     shptr = nullptr ; // frees the object 
     shptr = make_shared<T>() ; // frees the original object and points to new object

使用引用計數來實現,以跟踪有多少“東西”指向指針指向的對象。 當這個計數變為0時,該對像被自動刪除,即當指向該對象的所有share_ptr超出範圍時被刪除。 這消除了必須刪除使用新分配的對象的麻煩。

弱指針:幫助處理使用共享指針時產生的循環引用如果有兩個對象由兩個共享指針指向,並且有一個內部共享指針指向彼此共享指針,則會有循環引用,並且對像不會當共享指針超出範圍時被刪除。 要解決這個問題,請將內部成員從shared_ptr更改為weak_ptr。 注意:要訪問由弱指針指向的元素,使用lock(),這將返回一個weak_ptr。

T a ; 
shared_ptr<T> shr = make_shared<T>() ; 
weak_ptr<T> wk = shr ; // initialize a weak_ptr from a shared_ptr 
wk.lock()->memFn() ; // use lock to get a shared_ptr 
//   ^^^ Can lead to exception if the shared ptr has gone out of scope
if(!wk.expired()) wk.lock()->memFn() ;
// Check if shared ptr has gone out of scope before access

請參閱: std :: weak_ptr何時有用?

獨特的指針:獨有的輕量級智能指針。 當指針指向唯一對象而不共享指針之間的對象時使用。

unique_ptr<T> uptr(new T);
uptr->memFn(); 

//T * ptr = uptr.release(); // uptr becomes null and object is pointed to by ptr
uptr.reset() ; // deletes the object pointed to by uptr 

要更改唯一ptr指向的對象,請使用移動語義

unique_ptr<T> uptr1(new T);
unique_ptr<T> uptr2(new T);
uptr2 = std::move(uptr1); 
// object pointed by uptr2 is deleted and 
// object pointed by uptr1 is pointed to by uptr2
// uptr1 becomes null 

引用:它們本質上可以是const指針,即一個const指針,不能用更好的語法移動。

請參閱: C ++中的指針變量和引用變量之間的區別是什麼?

r-value reference : reference to a temporary object   
l-value reference : reference to an object whose address can be obtained
const reference : reference to a data type which is const and cannot be modified 

參考: https://www.youtube.com/channel/UCEOGtxYTB6vo6MQ-WQ9W_nQ : https://www.youtube.com/channel/UCEOGtxYTB6vo6MQ-WQ9W_nQ感謝安德烈指出這個問題。







c++-faq