C++智能指针auto_ptr

0. 引言

智能指针auto_ptr是在C++98中被提出,当时还没有右值引用移动语义的概念,所以到了C++11开始被弃用,在C++17就被移除了。本篇文章之所以要介绍auto_ptr是为了通过它进一步引出unique_ptrshared_ptrweak_ptr

1. 智能指针要解决的主要问题

1.1. 内存泄漏

在解释内存泄漏之前,首先要清楚栈内存堆内存在使用上的区别。存储在栈上面的数据,生命周期由操作系统进行管理,无需人工介入;存储在堆上面的数据,需要程序员手动申请和释放,由程序员负责。内存泄漏就是程序员手动申请了堆内存,由于某种原因程序未释放或无法释放导致这块堆内存没能释放掉。内存泄漏又可以细分为一下两类:

  1. 堆内存泄露:在堆上申请空间,未释放,导致该块内存不会被再次使用;
  2. 资源泄露:通常是指系统资源,如套接字、文件描述符等,由于这些资源在系统中都是有限的,如果创建了不归还就会耗尽资源。

1.2. 裸指针带来的问题

  1. 难以区分指针是指向一个对象还是一组对象;
  2. 难以确定指针在何时应该被销毁;
  3. 销毁指针应该用delete还是delete[],或者是其他销毁机制如fclose(),这需要追溯到最初创建该指针的位置;
  4. 在复杂的工程中,很难保证裸指针有且仅有一次销毁操作。

2. 什么是智能指针

简单来说,智能指针是一个自定义的类型,智能指针对象中包含一个指针,此指针指向动态开辟的空间,在智能指针对象离开作用域后,析构函数会自动调用删除器(Deleter,auto_ptr不具备删除器),释放所申请的资源。这种设计思想来源于RAII(Resource acquisition is initialization),它充分利用了C++语言局部对象自动销毁的特性来控制资源的生命周期。RAII过程可以被总结为四个步骤如下:

  1. 设计一个类来封装资源;
  2. 在构造函数中对资源进行申请;
  3. 在析构函数中对资源进行释放;
  4. 使用时定义一个该类的局部对象。

这里有必要解释一下删除器是什么。其实,删除器本身也是一个类,不过在这个类中主要是实现了一个operator()(type*)函数,通过该函数来释放指针(type*)。比如,有一个删除器对象Deleter,可以通过Deleter(ptr)的方式对指针ptr进行堆空间或资源的释放。

3. auto_ptr分析

auto_ptr类型包含的主要成员函数有构造函数、析构函数、operator=、get、operator*、operator->、reset和release等,下面的代码对这些函数进行了简单仿写,展示了auto_ptr的使用特点。

template<class _Tp>
class auto_ptr
{
public:
	using element_type = _Tp;
	using pointer = _Tp*;
private:
	pointer _M_ptr;
public:
	explicit auto_ptr(pointer _P = 0) :_M_ptr(_P) {}

	~auto_ptr() { delete _M_ptr; }
	//返回指向被管理对象的指针
	pointer get() const
	{
		return _M_ptr;
	}
	//替换被管理对象
	void reset(pointer _P = nullptr)
	{
		delete _M_ptr;
		_M_ptr = _P;
	}
	//释放被管理对象的所有权
	pointer release()
	{
		pointer _tmp = _M_ptr;
		_M_ptr = nullptr;
		return _tmp;
	}
	//以解引用的方式访问被管理对象
	_Tp& operator*() const
	{
		return *_M_ptr;
	}
	//以指针的方式访问被管理对象
	pointer operator->() const
	{
		return _M_ptr;
	}
	
	auto_ptr(auto_ptr& _Y) :_M_ptr(_Y.release()) {}
	//从另一个auto_ptr转移所有权
	auto_ptr& operator=(auto_ptr& _Y)
	{
		if (this != &_Y)
		{
			reset(_Y.release());
		}
		return *this;
	}
};

从上面实现的代码可以看出,auto_ptr在构造函数中获取资源,在析构函数中释放资源,这样就有效避免了内存泄露的问题。由于C++98还未引入右值引用以及移动语义,这就导致auto_ptr的拷贝构造和赋值陷入困境。

  1. 浅拷贝和浅赋值会带来重复释放的问题;
  2. 深拷贝和深赋值会带来语义上的问题。

最终auto_ptr做了语义上的牺牲,自己实现了移动拷贝和移动赋值,称为资源所有权的转移,这就导致了auto_ptr的拷贝构造和赋值与一般类型的不同,参数都是auto_ptr&而不是const auto_ptr&,这样直接就把资源移跑了。
auto_ptr主要存在三方面的问题:

  1. 拷贝和赋值会改变资源的所有权,这不符合一般性指针的用法;
  2. 无法与STL中的容器结合使用,容器中元素应该支持拷贝和赋值;
  3. 由于auto_ptr的析构函数中直接使用的delete来释放资源,所有auto_ptr无法处理对象数组。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值