1. 程式人生 > >智慧指標(smart pointer)(2):unique_ptr

智慧指標(smart pointer)(2):unique_ptr

Unique pointer:
  Manages the storage of a pointer, providing a limited garbage-collection facility, with little to no overhead over built-in pointers (depending on the deleter used).
   These objects have the ability of taking ownership of a pointer: once they take ownership they manage the pointed object by becoming responsible for its deletion at some point.
   unique_ptr objects automatically delete the object they manage (using a deleter) as soon as they themselves are destroyed, or as soon as their value changes either by an assignment operation or by an explicit call to unique_ptr::reset.
   unique_ptr objects own their pointer uniquely: no other facility shall take care of deleting the object, and thus no other managed pointer should point to its managed object, since as soon as they have to, unique_ptr objects delete their managed object without taking into account whether other pointers still point to the same object or not, and thus leaving any other pointers that point there as pointing to an invalid location.

   A unique_ptr object has two components:
  • a stored pointer: the pointer to the object it manages. This is set on construction, can be altered by anassignment operation or by calling member reset, and can be individually accessed for reading using members get or release.
  • a stored deleter: a callable object that takes an argument of the same type as the stored pointer and is called to delete the managed object. It is set on construction, can be altered by an assignment operation, and can be individually accessed using member get_deleter.

  unique_ptr objects replicate a limited pointer functionality by providing access to its managed object through operators * and -> (for individual objects), or operator [] (for array objects). For safety reasons, they do not support pointer arithmetics, and only support move assignment (disabling copy assignments).

  來看程式碼:

template <typename _Tp, typename _Tp_Deleter = default_delete<_Tp> > 
  class unique_ptr
  {
  public:
    typedef _Tp*               pointer;
    typedef _Tp                element_type;      
    typedef _Tp_Deleter        deleter_type;

    // Constructors.
    unique_ptr()
    : _M_t(pointer(), deleter_type())
    { static_assert(!std::is_pointer<deleter_type>::value,
            "constructed with null function pointer deleter"); }

    explicit
    unique_ptr(pointer __p)
    : _M_t(__p, deleter_type())
    { static_assert(!std::is_pointer<deleter_type>::value,
           "constructed with null function pointer deleter"); }

    unique_ptr(pointer __p,
        typename std::conditional<std::is_reference<deleter_type>::value, 
          deleter_type, const deleter_type&>::type __d)
    : _M_t(__p, __d) { }

    unique_ptr(pointer __p,
        typename std::remove_reference<deleter_type>::type&& __d)
    : _M_t(std::move(__p), std::move(__d))
    { static_assert(!std::is_reference<deleter_type>::value, 
            "rvalue deleter bound to reference"); }

    // Move constructors.
    unique_ptr(unique_ptr&& __u) 
    : _M_t(__u.release(), std::forward<deleter_type>(__u.get_deleter())) { }

    template<typename _Up, typename _Up_Deleter> 
      unique_ptr(unique_ptr<_Up, _Up_Deleter>&& __u) 
      : _M_t(__u.release(), std::forward<deleter_type>(__u.get_deleter()))
  { }

    // Destructor.
    ~unique_ptr() { reset(); }

    // Assignment.
    unique_ptr&
    operator=(unique_ptr&& __u)
    { 
      reset(__u.release()); 
      get_deleter() = std::move(__u.get_deleter()); 
      return *this;
    }

    template<typename _Up, typename _Up_Deleter> 
      unique_ptr&
      operator=(unique_ptr<_Up, _Up_Deleter>&& __u)
  {
        reset(__u.release()); 
        get_deleter() = std::move(__u.get_deleter()); 
        return *this;
      }

    unique_ptr&
    operator=(__unspecified_pointer_type) 
    {
  reset();
  return *this;
    }

    // Observers.
    typename std::add_lvalue_reference<element_type>::type operator*() const
    {
  _GLIBCXX_DEBUG_ASSERT(get() != 0);
  return *get();
    }

    pointer
    operator->() const
    {
  _GLIBCXX_DEBUG_ASSERT(get() != 0);
  return get();
    }

    pointer
    get() const
    { return std::get<0>(_M_t); }

    typename std::add_lvalue_reference<deleter_type>::type
    get_deleter()
    { return std::get<1>(_M_t); }

    typename std::add_lvalue_reference<
        typename std::add_const<deleter_type>::type
            >::type
    get_deleter() const
    { return std::get<1>(_M_t); }

    operator __unspecified_bool_type () const
    { return get() == 0 ? 0 : &unique_ptr::_M_t; }

    // Modifiers.
    pointer
    release() 
    {
  pointer __p = get();
  std::get<0>(_M_t) = 0;
  return __p;
    }

    void
    reset(pointer __p = pointer())
    {
  if (__p != get())
    {
      get_deleter()(get());
      std::get<0>(_M_t) = __p;
    }
    }

    void
    swap(unique_ptr&& __u)
    {
  using std::swap;
  swap(_M_t, __u._M_t);
    }

    // Disable copy from lvalue.
    unique_ptr(const unique_ptr&) = delete;

    template<typename _Up, typename _Up_Deleter> 
      unique_ptr(const unique_ptr<_Up, _Up_Deleter>&) = delete;

    unique_ptr& operator=(const unique_ptr&) = delete;

    template<typename _Up, typename _Up_Deleter> 
      unique_ptr& operator=(const unique_ptr<_Up, _Up_Deleter>&) = delete;

  private:
    __tuple_type _M_t;
};

  上面的程式碼禁用了拷貝構造和賦值操作,所以在使用時必須強調使用move語義或右值引用,否則會報錯。

總結:
  unique_ptr是一個獨享所有權的智慧指標,它提供了一種嚴格語義上的所有權,包括:
  1、擁有它所指向的物件。
  2、無法進行復制構造,也無法進行復制賦值操作。也就是說,我們無法得到指向同一個物件的兩個unique_ptr。但是可以進行移動構造和移動賦值操作。
  3、儲存指向某個物件的指標,當它本身被刪除釋放的時候(比如,離開了某個作用域),會使用給定的刪除器釋放它指向的物件。
  使用unique_ptr,可以實現以下功能,包括:
  1、為動態申請的記憶體提供異常安全。
  2、將動態申請記憶體的所有權傳遞給某個函式。
  3、從某個函式返回動態申請記憶體的所有權。
  4、在容器中儲存指標。
  5、所有auto_ptr應該具有的(但無法在C++ 03中實現的)功能
  至於unique_ptr和auto_ptr區別主要是在語義上,而且在必須交換控制權的情況下(非右值),unique_ptr會顯式的要求執行move操作,否則它會對使用者提出警示,這避免一些無心的拷貝發生。第二個區別是unique_ptr更加的高效,因為他使用的是右值引用,而不是拷貝構造。 
  auto_ptr解構函式只是單純的delete掉raw指標,而unique_ptr則可以定製自己的deleter,來指定unique_ptr析構時需要做哪些工作。

// unique_ptr destructor example
#include <iostream>
#include <memory>

int main () {
  auto deleter = [](int*p){
    delete p;
    std::cout << "[deleter called]\n";
  };

  std::unique_ptr<int,decltype(deleter)> foo (new int,deleter);

  std::cout << "foo " << (foo?"is not":"is") << " empty\n";

  return 0;                        // [deleter called]
}
Output:
foo is not empty
[deleter called]

  c++11中引入的move語義使得unique_ptr可以存放到容器中,參考這篇文章(http://www.th7.cn/Program/cp/201408/267890.shtml)。使用move就表示放棄對該物件的所有權,但並不對raw指標進行釋放。經過move函式呼叫後,失去了對raw指標的所有權,並未釋放raw指標,之後如果誤用了原來的unique_ptr,會導致undefine行為。

相關推薦

智慧指標(smart pointer)(2):unique_ptr

Unique pointer:   Manages the storage of a pointer, providing a limited garbage-collection facility, with little to no overhead ov

c++智慧指標(smart pointer)詳解

Smart Pointer Deal with c++11’s smart pointer facility. brief Smart pointers are class objects that behave like built-in

C++中的智慧指標(smart pointer)

指標問題是在學習C++,以及運用C++進行軟體開發過程中經常碰到的問題。其中之一,就是“懸垂指標”。所謂懸垂指標,就是是指指標指向了一塊沒有分配給使用者使用的記憶體,結果未定義,往往導致程式錯誤,而且難以檢測。 用一個簡單的例子來說明懸垂指標: string *sp = n

【C++】智慧指標(Smart Pointer)

1. 傳統指標存在的問題 傳統指標存在諸多的問題,比如指標所指向的物件的生命週期問題,掛起引用(dangling references),以及記憶體洩露(memory leaks). 如下是一個傳統指標的使用過程 void Foo() {

C++ 智慧指標(Smart Pointer)

智慧指標具有非常強大的能力,謹慎而明智的選擇能帶來極大的好處。我不否認智慧指標的能力,雖然我在之前的否認過auto_ptr。可能由於我自身能力的限制,體會不到auto_ptr的好處,但這樣的可能性我覺得已經不大了。但auto_ptr是最簡單的智慧指標,在它的周圍存在大量的作品

[C++] 什麼是智慧指標(Smart Pointer)以及何時使用

答案 1 智慧指標是一個類,它封裝了一個原始的C++指標,以管理所指物件的生命期。沒有單一的智慧指標型別,但所有這些都嘗試以實用的方式抽象原始指標。 智慧指標應優先於原始指標。 如果你覺得你需要使用指標(首先要考慮你是否真的需要指標),你通常會想要使用智慧指

深度探索智慧指標(Smart Pointer)

主題索引: 一、剖析C++標準庫智慧指標(std::auto_ptr)        1.Do you Smart Pointer?    2.std::auto_ptr的設計原理    3.std::auto_ptr高階使用指南    4.你是否覺得std::auto_p

C++深度探索系列:智慧指標(Smart Pointer) [二]

                                           深度探索智慧指標(Smart Pointer) 主題索引: 一、剖析C++標準庫智慧指標(std::auto_ptr)        1.Do you Smart Pointer?    2

智慧指標(Smart Pointers)

share_prt類 share_prt 也是一個類模板,使用時和vector一樣要說明型別。 shared_prt<string> p1; //這是一個指向string型別的智慧指標,使用預設建構函式初始化智慧指標,裡面含有一個NULL指標。 智慧

C++11 智慧指標std::shared_ptr/std::unique_ptr/std::weak_ptr

std::shared_ptr std::shared_ptr 是一種智慧指標,它能夠記錄多少個 shared_ptr 共同指向一個物件,從而消除顯示的呼叫 delete,當引用計數變為零的時候就

實現智慧指標Smart Pointer

#ifndef SMARTPTR_H_ #define SMARTPTR_H_ template<typename T> struct SmartPtr { explicit Smart

SMART POINTER智慧指標

智慧指標(smart pointer):智慧指標是其實是一個物件A,它帶有一個模針成員變數m_p.用該A管理m_p,通過這種管理機制,可以防止由於new而導致的記憶體洩漏.智慧指標物件在使用時像指標一樣.同時也具有一般物件的所有特徵.如果要注意以下幾點:1,物件之間的賦值:若

c++11: 智慧指標 shared_ptr & unique_ptr

一、背景 1. 堆記憶體、棧記憶體、靜態區記憶體 我們知道,靜態記憶體用來儲存區域性 static 物件、類 static 資料成員以及定義在函式之外的變數。而棧記憶體用來儲存定義在函式內的非 static 物件。 分配在靜態區或棧記憶體中的物件由編譯器自動建立和銷燬,對於棧

C++ 面向物件高階開發--pointer-like classes(智慧指標

C++的類設計出來可以像 1.一個指標 2.像一個函式。 先來看1,為了比一般指標做更多的東西。 如圖:智慧指標中一定帶一個一般的指標,而且必須寫上* 和 -> 的方法。 【迭代器其實也是一種智慧指標】 如圖,link_type就是那個真正的指標,黃色部分就

C++智慧指標unique_ptr

從C++智慧指標之auto_ptr一文中得知:在使用auto_ptr時,可能會不經意的將多個auto_ptr指向同一塊記憶體,造成auto_ptr銷燬釋放時多次釋放同一塊記憶體。為了解決該問題,本文引出了unique_ptr。 顧名思義,unique是唯一的意思。說明它跟auto_p

淺談智慧指標auto_ptr/shared_ptr/unique_ptr

一.智慧指標 1.引入 我們通常使用類似new申請一塊空間,交由一個指標指向,假如說最後忘記delete,將會造成記憶體洩露。而智慧指標的出現,就是對這種問題的解決方式,智慧指標類似指標,卻可以用於管理動態分配的記憶體。本章所解說的是三種智慧指標: (1)C++98提

C++ 智慧指標(unique_ptr, shared_ptr)的原始碼分析

shared_ptr 標頭檔案 template <typename T> class SharedPointer { public: SharedPointer(T *ptr = nullptr, const std::function<void

C++11--智慧指標shared_ptr,weak_ptr,unique_ptr

共享指標 shared_ptr /*********** Shared_ptr ***********/ // 為什麼要使用智慧指標,直接使用裸指標經常會出現以下情況 // 1. 當指標的生命長於所指的資源:野指標 // 2. 當指標的生命短於所指的資源:資源洩漏 // // 智慧指標: 確保指標和資源的生

Boost學習系列2-智慧指標(上)

 一、概述    最先講的就是指標,這是C語言中,不少程式設計師害怕的東西,害怕的原因大多是因為不瞭解其初始化、呼叫、賦值和清除的方式,而智慧指標則可以去除這個顧慮,在初始化時就已經預定了刪除,排解了後顧之憂。1998年修訂的第一版C++標準只提供了一種智慧指標:std::a

C++11引入智慧指標std::unique_ptr

包含標頭檔案<memery> 名稱空間:std/* 不是std::tr1 */ 我們知道auto_ptr通過複製構造或者通過=賦值後,原來的auto_ptr物件就報廢了.所有權轉移到新的物件中去了.而通過shared_ptr可以讓多個智慧指標物件同時擁有