智能指针(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行为。

时间: 2024-10-25 08:08:09

智能指针(smart pointer)(2):unique_ptr的相关文章

【C++】智能指针(Smart Pointer)

1. 传统指针存在的问题 传统指针存在诸多的问题,比如指针所指向的对象的生命周期问题,挂起引用(dangling references),以及内存泄露(memory leaks). 如下是一个传统指针的使用过程 void Foo() { int *iPtr = new int[5]; // manipulate the memory block // ... // ... // ... delete[] iPtr; } 以上代码将正常运行且内存将被合理释放,但是使用指针常会发生一些意想不到的事情

智能指针(smart pointer)(1):auto_ptr

智能指针解决了资源生存期管理的问题(尤其是动态分配的对象).智能指针有各种不同的风格.多数都有一种共同的关键特性:自动资源管理.这种特性可能以不同的方式出现:如动态分配对象的生存期控制,和获取及释放资源 (文件, 网络连接).这里主要讨论第一种情况,它们保存指向动态分配对象的指针,并在正确的时候删除这些对象. 何时我们需要智能指针? 有三种典型的情况适合使用智能指针: ? 资源所有权的共享 ? 要写异常安全的代码时 ? 避免常见的错误,如资源泄漏 共享所有权,当多个对象需要同时使用第三个对象的情

(转)Delphi2009初体验 - 语言篇 - 智能指针(Smart Pointer)的实现

快速导航 一. 回顾历史二. 智能指针简介三. Delphi中的interface四. Delphi中智能指针的实现五. interface + 泛型 = 强类型的智能指针!六. 智能指针与集合七. 注意事项八. 总结 本随笔所有源代码打包下载 一.回顾历史 在c++中,对象可以创建在栈里,也可以创建在堆里.如: class CTestClass{public: CTestClass() { printf("Create"); } void DoPrint() {} ~CTestCla

C++深度探索系列:智能指针(Smart Pointer) [一]

阿里妹导读:以深度学习为代表的人工智能在图像.语音和NLP领域带来了突破性的进展,在信息检索和个性化领域近几年也有不少公开文献,比如wide& deep实现了深度模型和浅层模型的结合,dssm用于计算语义相关性,deepfm增加了特征组合的能力,deep CF用深度学习实现协同过滤,rnn recommender 采用行为序列预估实现个性化推荐等. 工业级的信息检索或个性化系统是一个复杂的系统工程,深度学习的工业级应用需要具备三个条件:强大的系统计算能力,优秀的模型设计能力和合适的应用场景.今天

c++11 智能指针 unique_ptr、shared_ptr与weak_ptr

c++11 智能指针 unique_ptr.shared_ptr与weak_ptr C++11中有unique_ptr.shared_ptr与weak_ptr等智能指针(smart pointer),定义在<memory>中. 可以对动态资源进行管理,保证任何情况下,已构造的对象最终会销毁,即它的析构函数最终会被调用. unique_ptr unique_ptr持有对对象的独有权,同一时刻只能有一个unique_ptr指向给定对象(通过禁止拷贝语义.只有移动语义来实现). unique_ptr

C++ 资源管理(RAII)--智能指针

1. 智能指针(Smart Pointer) i. 是存储指向动态分配(堆)对象指针的类 ii. 在面对异常的时候格外有用,因为他们能够确保正确的销毁动态分配的对象 iii. RAII类模拟智能指针,见备注 2. C++11提供了以下几种智能指针,位于头文件<memory>,它们都是模板类 i. std::auto_ptr(复制/赋值) ii. std::unique_ptr  c++11 iii.std::shared_ptr  c++11 iv.std::weak_ptr    c++11

Qt 智能指针学习(7种QT智能指针和4种std智能指针)

从内存泄露开始? 很简单的入门程序,应该比较熟悉吧 ^_^ #include <QApplication> #include <QLabel> int main(int argc, char *argv[]) { QApplication app(argc, argv); QLabel *label = new QLabel("Hello Dbzhang800!"); label->show(); return app.exec(); } 在  从 Qt

Qt 智能指针学习(7种QT的特有指针)

从内存泄露开始? 很简单的入门程序,应该比较熟悉吧 ^_^ #include <QApplication> #include <QLabel> int main(int argc, char *argv[]) { QApplication app(argc, argv); QLabel *label = new QLabel("Hello Dbzhang800!"); label->show(); return app.exec(); } 在  从 Qt

Qt 智能指针学习

从内存泄露开始? 很简单的入门程序,应该比较熟悉吧 ^_^ #include <QApplication> #include <QLabel> int main(int argc, char *argv[]) { QApplication app(argc, argv); QLabel *label = new QLabel("Hello Dbzhang800!"); label->show(); return app.exec(); } 在  从 Qt