智能指针 -- unique_ptr

源码分析

源码链接: gcc/unique_ptr.h at master · gcc-mirror/gcc · GitHub

上面链接中的源码是unique_ptr的完整定义,我们来简化其类结构看看:

template <typename _Tp, typename _Dp = default_delete<_Tp>>
class unique_ptr
{
  __uniq_ptr_data<_Tp, _Dp> _M_t;

  public:
    using pointer = typename __uniq_ptr_impl<_Tp, _Dp>::pointer;
    using element_type  = _Tp;
    using deleter_type  = _Dp;

    //构造函数
    template<typename _Del = _Dp, typename = _DeleterConstraint<_Del>>
	constexpr unique_ptr() noexcept: _M_t() { }
    
    template<typename _Del = _Dp, typename = _DeleterConstraint<_Del>>
    unique_ptr(pointer __p) noexcept : _M_t(__p) { }

    template<typename _Del = deleter_type,typename = _Require<is_copy_constructible<_Del>>>
    unique_ptr(pointer __p, const deleter_type& __d) noexcept : _M_t(__p, __d) {  }

    template<typename _Del = deleter_type, typename = _Require<is_move_constructible<_Del>>>
    unique_ptr(pointer __p,__enable_if_t<!is_lvalue_reference<_Del>::value,_Del&&> __d) 
    noexcept : _M_t(__p, std::move(__d)) { }

    template<typename _Del = deleter_type,typename _DelUnref = typename remove_reference<_Del>::type>
    unique_ptr(pointer,__enable_if_t<is_lvalue_reference<_Del>::value, _DelUnref&&>) = delete;

	constexpr unique_ptr(nullptr_t) noexcept : _M_t() { }

    // 移动构造函数
    unique_ptr(unique_ptr&&) = default;

    unique_ptr(unique_ptr<_Up, _Ep>&& __u) 
    noexcept : _M_t(__u.release(), std::forward<_Ep>(__u.get_deleter())) {  }

    // 析构函数
    ~unique_ptr() noexcept {
	  auto& __ptr = _M_t._M_ptr();
	  if (__ptr != nullptr)
	    get_deleter()(std::move(__ptr));
	    __ptr = pointer();
    }

    // 操作符 = 
    unique_ptr& operator=(unique_ptr&&) = default;

    template<typename _Up, typename _Ep>
    typename enable_if< __and_<__safe_conversion_up<_Up, _Ep>,is_assignable<deleter_type&, _Ep&&>>::value,unique_ptr&>::type
    operator=(unique_ptr<_Up, _Ep>&& __u) noexcept {
	  reset(__u.release());
	  get_deleter() = std::forward<_Ep>(__u.get_deleter());
	  return *this;
    }

  unique_ptr& operator=(nullptr_t) noexcept {
	 reset();
	 return *this;
  }
  
  //操作符 *
  typename add_lvalue_reference<element_type>::type
  operator*() const noexcept(noexcept(*std::declval<pointer>())) {
	  return *get();
  }
  
  //操作符 ->
  pointer operator->() const noexcept {
	  _GLIBCXX_DEBUG_PEDASSERT(get() != pointer());
	  return get();
  }

  // 获取指针
  pointer get() const noexcept
  { return _M_t._M_ptr(); }

  // 获取deleter函数
  deleter_type& get_deleter() noexcept
  { return _M_t._M_deleter(); }
   
  const deleter_type& get_deleter() const noexcept { 
      return _M_t._M_deleter(); 
  }

  // 操作符 取bool
  explicit operator bool() const noexcept { 
       return get() == pointer() ? false : true; 
  }
  // release接口
  pointer release() noexcept { return _M_t.release(); }
  //reset接口
  void reset(pointer __p = pointer()) noexcept {
	 _M_t.reset(std::move(__p));
  }
  //swap接口
  void swap(unique_ptr& __u) noexcept {
	_M_t.swap(__u._M_t);
  }

   // 禁用从lvalue拷贝操作.
   unique_ptr(const unique_ptr&) = delete;
   unique_ptr& operator=(const unique_ptr&) = delete;
};

代码示例1

默认构造函数,操作符(), 以及get(),操作符->

#include <iostream>
#include<memory>
using namespace std;

class Car {
public:
    Car() {
        cout << "consutrct car" << endl;
    }
    virtual ~Car() {
        cout << "desconsturct car" << endl;
    }
    void ShowInfo() {
        cout << __func__ << ": this is base Car class" << endl;
    }

    void ShowInfoWithMember() {
        cout << __func__ << ": var_test_:" << var_test_;
    }
private:
    int var_test_ = 0;
};

int main() {
    std::unique_ptr<Car> aa;
    if (!aa) {
      cout << "aa is not nullptr" << endl;
    }

    if (!aa.get()) {
        cout << "aa.get() is not nullptr" << endl;
    }

    aa->ShowInfo();

    aa->ShowInfoWithMember()

}

示例运行结果:

 过程分析:

那么该示例实际都调用了unique_ptr这个类中的哪些接口呢 ?

默认构造函数

std::unique_ptr<Car> aa;

上述语句会调用unique_ptr的默认构造函数:

 template<typename _Del = _Dp, typename = _DeleterConstraint<_Del>>
 constexpr unique_ptr() noexcept : _M_t()  { }

可以看到class unique_ptr中有个成员_M_t:

template <typename _Tp, typename _Dp = default_delete<_Tp>>
class unique_ptr
{
   ..............;
   __uniq_ptr_data<_Tp, _Dp> _M_t;
   ..............;
};

template <typename _Tp, typename _Dp>
struct __uniq_ptr_data<_Tp, _Dp, true, false> : __uniq_ptr_impl<_Tp, _Dp>
{
   using __uniq_ptr_impl<_Tp, _Dp>::__uniq_ptr_impl;
   __uniq_ptr_data(__uniq_ptr_data&&) = default;
   __uniq_ptr_data& operator=(__uniq_ptr_data&&) = delete;
};

template <typename _Tp, typename _Dp>
class __uniq_ptr_impl
{
  template <typename _Up, typename _Ep, typename = void>
  struct _Ptr {
    using type = _Up*;
  };

  template <typename _Up, typename _Ep>
  struct _Ptr<_Up, _Ep, __void_t<typename remove_reference<_Ep>::type::pointer>>
  {
    using type = typename remove_reference<_Ep>::type::pointer;
  };

  public:
    using _DeleterConstraint =
      enable_if<__and_<__not_<is_pointer<_Dp>>,is_default_constructible<_Dp>>::value>;

    using pointer = typename _Ptr<_Tp, _Dp>::type;

    __uniq_ptr_impl() = default;
    __uniq_ptr_impl(pointer __p) : _M_t() { _M_ptr() = __p; }

    template<typename _Del>
	__uniq_ptr_impl(pointer __p, _Del&& __d) : _M_t(__p, std::forward<_Del>(__d)) { }

    __uniq_ptr_impl(__uniq_ptr_impl&& __u) noexcept: _M_t(std::move(__u._M_t))
    { __u._M_ptr() = nullptr; }

    __uniq_ptr_impl& operator=(__uniq_ptr_impl&& __u) noexcept {
	  reset(__u.release());
	  _M_deleter() = std::forward<_Dp>(__u._M_deleter());
	  return *this;
    }

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

    _Dp& _M_deleter() noexcept { return std::get<1>(_M_t); }
    const _Dp& _M_deleter() const noexcept { return std::get<1>(_M_t); }

    void reset(pointer __p) noexcept {
	  const pointer __old_p = _M_ptr();
	  _M_ptr() = __p;
	  if (__old_p)
	    _M_deleter()(__old_p);
    }

    pointer release() noexcept {
	  pointer __p = _M_ptr();
	  _M_ptr() = nullptr;
	  return __p;
    }

    void swap(__uniq_ptr_impl& __rhs) noexcept
    {
	  using std::swap;
	  swap(this->_M_ptr(), __rhs._M_ptr());
	  swap(this->_M_deleter(), __rhs._M_deleter());
    }

    private:
      tuple<pointer, _Dp> _M_t;
};

 通过上述代码可以看出unique_ptr() :: _M_t() 这个语句会调用到 __uniq_ptr_impl() = default; 而__uniq_ptr_impl中有一个成员:tuple<pointer, _Dp> _M_t;

操作符()

if (!aa.get()) {
        cout << "aa.get() is nullptr" << endl;
    }
 

上述语句会调用到unique_ptr::get()

  pointer get() const noexcept
  { return _M_t._M_ptr(); }

再调用到__uniq_ptr_impl::_M_ptr()

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

最终取出的是tuple<pointer, _Dp> _M_t中的pointer这个指针,此例子中应该是Car* 类型,因为默认构造函数没有对tuple<pointer, _Dp> _M_t中的pointer这个指针赋值,所以unique_ptr::get()返回的就是空指针了。

操作符->

aa->ShowInfo();

上述代码实际上调用的是unique_ptr这个类中的操作符->

  pointer operator->() const noexcept {
	  _GLIBCXX_DEBUG_PEDASSERT(get() != pointer());
	  return get();
  }

然而此测试代码中unique_ptr.get() 返回的是空,那为什么调用ShowInfo()函数不会crash ?

而调用ShowInfoWithMember() 这个接口就crash了呢 ?

void Car::ShowInfoWithMember() {
        cout << __func__ << ": var_test_:" << var_test_;
}
aa->ShowInfoWithMember()

这篇博客就解释的很到位:C++---空指针能调用类成员函数吗?_shanghx_123的博客-CSDN博客_空指针调用成员函数

空指针为什么能调用成员函数?
对于类成员函数而言,并不是一个对象对应一个单独的成员函数体,而是此类的所有对象共用这个成员函数体。 当程序被编译之后,此成员函数地址即已确定。当调用p->func1(); 这句话时,其实就是调用A::func1(this);而成员函数的地址在编译时就已经确定, 需要注意的是,你用空指针调用成员函数,只是让this指针指向了空,所以空指针也是可以调用普通成员函数,只不过此时的this指针指向空而已,但函数fun1函数体内并没有用到this指针,所以不会出现问题。
当成员函数体内用到this指针时,如果你的this指针是空,那么程序就会崩溃。

代码示例2 

reset() , release()函数

class Car {
public:
    Car() {
        cout << "consutrct car" << endl;
    }
    virtual ~Car() {
        cout << "desconsturct car" << endl;
    }
    void ShowInfo(string ss) {
        cout << __func__ << "," << ss << endl;
    }
    void ShowInfoWithMember(string ss) {
        cout << __func__ << "," << ss << ", var_test_:" << var_test_ << endl;
    }
private:
    int var_test_ = 0;
};

auto DelCar = [] (Car *car) {
                  cout << "this is in DelCar function" << endl;
                  delete car;
              };

int main() {
    Car* pTest = new Car();
    std::unique_ptr<Car, decltype(DelCar)> aa(pTest, DelCar);
    if (aa.get()) {
        cout << "main: aa.get() is not nullptr" << endl;
    } else {
        cout << "main: aa.get() is nullptr" << endl;
    }
    aa->ShowInfo("aa");
    aa->ShowInfoWithMember("aa");

    std::unique_ptr<Car, decltype(DelCar)> bb(nullptr, DelCar);
    cout << "bb.reset" << endl;
    bb.reset(pTest);

    if (bb.get()) {
        cout << "main: bb.get() is not nullptr" << endl;
    } else {
        cout << "main: bb.get() is nullptr" << endl;
    }
    if (aa.get()) {
        cout << "main: aa.get() is not nullptr after reset bb" << endl;
    } else {
        cout << "main: aa.get() is nullptr after reset bb" << endl;
    }
    aa->ShowInfo("bb");
    aa->ShowInfoWithMember("bb");
}

代码运行结果:

 上述示例代码主要是示例带删除函数的unique_ptr的构造函数以及unique_ptr::reset()函数:

auto DelCar = [] (Car *car) {
                  cout << "this is in DelCar function" << endl;
                  delete car;
              };
Car* pTest = new Car();
std::unique_ptr<Car, decltype(DelCar)> aa(pTest, DelCar);

上面代码会调用的unique_ptr的构造函数: 

 template<typename _Del = deleter_type,
          typename = _Require<is_copy_constructible<_Del>>>
 unique_ptr(pointer __p, const deleter_type& __d) noexcept : _M_t(__p, __d) {  }

 reset函数

std::unique_ptr<Car, decltype(DelCar)> bb(nullptr, DelCar);
cout << "bb.reset" << endl;
bb.reset(pTest);

上述代码会调用到unique_ptr的reset函数:

  void reset(pointer __p = pointer()) noexcept {
	 _M_t.reset(std::move(__p));
  }

然后会调用到__uniq_ptr_impl::reset

tuple<pointer, _Dp> _M_t;
pointer& _M_ptr() noexcept { return std::get<0>(_M_t); }

void reset(pointer __p) noexcept {
	const pointer __old_p = _M_ptr();
	_M_ptr() = __p;
	if (__old_p)
	  _M_deleter()(__old_p);
}

通过__uniq_ptr_impl的reset函数源码可以知道,reset会先将old_ptr取出来,如果old_ptr不为空则向调用deleter所释放掉old_ptr, 然后将reset参数中新的ptr存入到tuple _M_t中。

使用pTest指针构造unique_ptr aa之后,再此使用pTest指针再构造一个unique_ptr bb会发生什么事情呢?

unique_ptr aa和bb 还是都在管理指针Car* pTest,并且都能正常访问pTest的成员函数和成员变量。只不过在aa析构的时候已经将pTest释放,而bb析构的时候再次释放pTest导致程序崩溃了。

 release()

那么上述代码,如何不让它崩溃呢 ?使用unique_ptr::release()

int main() {
    Car* pTest = new Car();
    std::unique_ptr<Car, decltype(DelCar)> aa(pTest, DelCar);

    ......................;
    aa->ShowInfo("aa");
    aa->ShowInfoWithMember("aa");

    std::unique_ptr<Car, decltype(DelCar)> bb(nullptr, DelCar);
    cout << "bb.reset" << endl;
    bb.reset(pTest);

    .......................;

    aa->ShowInfo("bb");
    aa->ShowInfoWithMember("bb");

    cout << "call aa.release()" << endl;
    aa.release();

}

程序运行结果:

将aa release一把后,程序就不会蹦了。

下面来看看release函数的实现:

pointer __uniq_ptr_impl::release() noexcept {
	  pointer __p = _M_ptr();
	  _M_ptr() = nullptr;
	  return __p;
}
pointer unique_ptr::release() noexcept { return _M_t.release(); }

 unique_ptr::release函数只是将unique_ptr中托管的指针置位nullptr,而不是像 unique_ptr::reset函数调用deleter将托管指针销毁。

unique_ptr拷贝构造函数和移动构造函数以及operator =

示例代码3

拷贝构造函数, operator =

class Car {
public:
    Car() {
        cout << "consutrct car" << endl;
    }
    virtual ~Car() {
        cout << "desconsturct car" << endl;
    }
    void ShowInfo(string ss) {
        cout << __func__ << "," << ss << endl;
    }
    void ShowInfoWithMember(string ss) {
        cout << __func__ << "," << ss << ", var_test_:" << var_test_ << endl;
    }
private:
    int var_test_ = 0;
};

void func(std::unique_ptr<Car> pp) {
    pp->ShowInfoWithMember("func_test");
}

unique_ptr<Car>  func2(std::unique_ptr<Car> &pp) {
  return pp;
}

int main() {
    Car* pTest = new Car();
    std::unique_ptr<Car> aa(pTest);
    //执行拷贝构造函数三种场景
    //1. 使用a对象初始化b对象
    std::unique_ptr<Car> bb(aa);

    //2. 函数参数
    func(aa); //aa 
    
    //3. 返回值
    unique_ptr cc = func2(aa);
    //上面这三种情况都会因调用拷贝构造函数,而编译报错
    
    std::unique_ptr<Car> bb(nullptr);
    bb = aa;  //会调用到operator = ,此处会编译报错
}

 从unique_ptr的定义来看,拷贝构造函数和operator = 是被删除禁用的。所以会编译失败。

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

移动构造函数和移动operator =

上面的代码编译错误如何修复呢 ?

int main() {
    Car* pTest = new Car();
    std::unique_ptr<Car> aa(pTest);
    // func(aa); //aa 作为函数参数,会调用到unique_ptr的拷贝构造函数,此时会编译报错
    func(std::move(aa));

    std::unique_ptr<Car> bb(nullptr);
    //bb = aa;  //会调用到operator = ,此处会编译报错
    bb = std::move(aa);
}

使用std::move(aa)之后将aa变成右值,编译就不会报错了。此时会调用unique_ptr中的以下函数:

template<typename _Up, 
         typename _Ep, 
         typename = _Require<__safe_conversion_up<_Up, _Ep>,
	                         __conditional_t<is_reference<_Dp>::value,
			                 is_same<_Ep, _Dp>,
			                 is_convertible<_Ep, _Dp>>>>
unique_ptr(unique_ptr<_Up, _Ep>&& __u) noexcept
	: _M_t(__u.release(), std::forward<_Ep>(__u.get_deleter()))
{ }


template<typename _Up, typename _Ep>
typename enable_if< __and_<__safe_conversion_up<_Up, _Ep>,
                                   is_assignable<deleter_type&, _Ep&&>
                                 >::value,unique_ptr&>::type
operator=(unique_ptr<_Up, _Ep>&& __u) noexcept
{
   reset(__u.release());
   get_deleter() = std::forward<_Ep>(__u.get_deleter());
   return *this;
}

pointer __uniq_ptr_impl::release() noexcept {
	  pointer __p = _M_ptr();
	  _M_ptr() = nullptr;
	  return __p;
}

从上面的代码可以看出__u.release()将__u中托管的指针先取出来,然后置为nullptr,再return。也就是将托管的指针所有权进行了转移。

代码示例4

deleter

#include <iostream>
#include<memory>
using namespace std;
class Car;
auto DelCarLamda = [] (Car *car) {
                  cout << "this is in DelCar function" << endl;
                  delete car;
};
struct DelCarStruct {
  void operator() (Car *car) const {
    if (car != nullptr) {
        delete car;
    }
  }
};
typedef void (*DelCarFuncType)(Car*);

void DelCarFunc(Car *car) {
  if (car != nullptr) {
    delete car;
  }
}
int main() {
    // need the second param: deleter var
    std::unique_ptr<Car, decltype(DelCarLamda)> ll(new Car(), DelCarLamda);
    cout << "use Lamda deleter, size:" << sizeof(ll) << endl;

    // need the second param: deleter var
    // 此种方式,unique_ptr的size会多一个指针变量的大小
    std::unique_ptr<Car, DelCarFuncType> ff(new Car(), DelCarFunc);
    cout << "use func pointer deleter, size:" << sizeof(ff) << endl;

    // no need the second param
    std::unique_ptr<Car, DelCarStruct> ss(new Car());
    cout << "use struct operator () deleter, size:" << sizeof(ss) << endl;
}

class Car {
public:
    Car() {
        cout << "consutrct car" << endl;
    }
    virtual ~Car() {
        cout << "desconsturct car" << endl;
    }
    void ShowInfo(string ss) {
        cout << __func__ << "," << ss << endl;
    }
    void ShowInfoWithMember(string ss) {
        cout << __func__ << "," << ss << ", var_test_:" << var_test_ << endl;
    }
private:
    int var_test_ = 0;
};

代码运行结果:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值