《智能指针》

一、智能指针解决的问题

        在程序设计中,我们经常会使用到堆空间。

	char* ptr = new char[5];
	delete[] ptr;

        向堆动态申请了内存,需要我们手动调用堆空间。这对程序设计造成不便,加入在设计过程中,某处动态申请了内存,但未手动回收,就会造成内存泄漏,导致程序运行一段时间后崩掉。

        智能指针就是为了处理这个问题诞生的。原理是,对于C++的对象来说,在创建时会调用其构造函数,而销毁时会调用其析构函数。利用这个特性,我们可以在创建对象的时候分配资源,在对象销毁时释放资源,这样在对象出作用域的时候便可以自动回收,这就是RAII(Resource Acquisition Is Initialization)的核心思想,即资源获取与对象创建同步资源释放与对象销毁同步

二、auto_ptr

        auto_ptr是C++98的指针,它主要的思想就是将指针封装成一个类,用对象的声明周期来管理指针所指的对空间。

       auto_ptr源码

class auto_ptr { // wrap an object pointer to ensure destruction
public:
    using element_type = _Ty;
    //构造函数
    explicit auto_ptr(_Ty* _Ptr = nullptr) noexcept : _Myptr(_Ptr) {}
    //拷贝构造
    auto_ptr(auto_ptr& _Right) noexcept : _Myptr(_Right.release()) {}

    //赋值重载
    auto_ptr& operator=(auto_ptr& _Right) noexcept {
        reset(_Right.release());
        return *this;
    }
    //释放权限
    _Ty* release() noexcept {
        _Ty* _Tmp = _Myptr;
        _Myptr    = nullptr;
        return _Tmp;
    }
    //赋值时要释放ptr2原来的资源
    void reset(_Ty* _Ptr = nullptr) noexcept { // destroy designated object and store new pointer
    if (_Ptr != _Myptr) {
        delete _Myptr;
    }

    _Myptr = _Ptr;
}

     ~auto_ptr() noexcept {
     delete _Myptr;
     }

private:
    _Ty* _Myptr; // the wrapped object pointer
}

        可以看到,在auto_ptr实际就是运用了一个模板类,不过这里面有一些问题。

  1. 当使用的时候,因为构造函数使用了关键字noexcept,所以不能讲裸指针直接转化成auto_ptr,必须要在定义是显式调用。
  2. 对于拷贝构造和赋值重载,这里只是做了简单的将右边的指针置空,并将左边的指针指向右边原来管理的内存。

        详细地看一下第二点,加入在设计程序时有如下的操作。

auto_ptr<MyClass> ptr1(new MyClass("this is auto_ptr"));
auto_ptr<MyClass> ptr2(new MyClass("auto_ptr"));
ptr2 = ptr1;

        这里申请了两块堆内存,分别用ptr1和ptr2来管理。使用ptr2 = ptr1会出现问题。因为ptr1的资源转交给ptr2了(ptr1已经置为空了),再对ptr1进行访问,会出现访问空指针的问题。

        如果在数组使用了auto_ptr,在将访问数组元素时,数组就会把元素的权限交出去,再也无法访问到该元素了。

 auto_ptr<string> films[5] =
 {
  auto_ptr<string> (new string("Fowl Balls")),
  auto_ptr<string> (new string("Duck Walks")),
  auto_ptr<string> (new string("Chicken Runs")),
  auto_ptr<string> (new string("Turkey Errors")),
  auto_ptr<string> (new string("Goose Eggs"))
 };
 auto_ptr<string> pwin;
 pwin = films[2]; // films[2] loses ownership. 将所有权从films[2]转让给pwin,此时films[2]不再引用该字符串从而变成空指针

 cout << "The nominees for best avian baseballl film are\n";
 for(int i = 0; i < 5; ++i)
  cout << *films[i] << endl;
 cout << "The winner is " << *pwin << endl;

        这个问题,必须要开发人员清楚auto_ptr的底层原理才能避免,这也是auto_ptr被摒弃的原理。

三、unique_ptr

        unique_ptr的出现主要解决auto_ptr在赋值和拷贝时的安全问题。

auto_ptr<MyClass> ptr1(new MyClass("this is auto_ptr"));
auto_ptr<MyClass> ptr2(new MyClass("auto_ptr"));
ptr2 = ptr1;

        为了解决这个问题,unique_ptr有两个操作:

  1. 禁用拷贝和赋值。
  2. 支持移动构造和移动赋值。

        unique_ptr源码

//禁用移动构造和赋值重载
unique_ptr(const unique_ptr&)            = delete;
unique_ptr& operator=(const unique_ptr&) = delete;

//
_CONSTEXPR23 unique_ptr& operator=(nullptr_t) noexcept {
    reset();
    return *this;
}

/*提供了右值引用的拷贝构造函数*/
unique_ptr(unique_ptr&& _Right) noexcept 
: _Mybase(_Right.release(),_STD forward<_Dx>(_Right.get_deleter()))
{	// construct by moving _Right  }

/*提供了右值引用的operator=赋值重载函数*/
unique_ptr& operator=(unique_ptr&& _Right) noexcept
{	// assign by moving _Right
	if (this != _STD addressof(_Right))
    {	// different, do the move
		reset(_Right.release());
		this->get_deleter() = _STD forward<_Dx>(_Right.get_deleter());
	}
	return (*this);
}

        unique_ptr处理思路是,auto_ptr的安全问题出在拷贝构造和上,那干脆就把这俩给禁用掉,把问题暴露在编译阶段。但是这样就有点局限了,所有又提供了移动构造和移动赋值。

为什么unique_ptr要提供移动呢?

        是因为对于左值来说,因为他的生命周期比较长,所以在后面可能还会访问到。但是右值都是一些声明周期比较短的临时变量或者要调用move交出权限的变量,用户非常清楚这变量后面不会再使用了,就不会出现把权限交出之后还要用这个临时变量去访问的情况。

四、shared_ptr和weak_ptr

shared_ptr智能指针源码分析-CSDN博客

C++智能指针学习——小谈引用计数 - paw5zx - 博客园 (cnblogs.com)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值