C++智能指针剖析

1. 什么是智能指针

智能指针是一种为解决在有些情况下,申请的内存资源不能的到及时释放的问题所衍生出来的一种类指针.

设计原理

智能指针采用了RAII的思想,利用类类型在作用域结束时调用析构函数完成对指针所指向资源的清理.

2. 智能指针的应用

前面说了智能指针是为了解决资源释放问题所产生的一种指针,所以,主要还是应用在内存泄漏等相关问题上
例如:
程序员在编写程序时,在堆上new或malloc出一块内存资源,因忘记delete或free而导致的内存泄漏.
在代码执行过程中,因异常而跳过内存资源的释放代码执行而导致的异常问题等.

内存泄漏的危害

内存泄漏的危害:长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死。

如何避免内存泄漏

1.养成良好的编码习惯,及时清理申请的资源.
2.使用RAII的思想管理申请的资源.
3. 可以使用一些检测内存泄漏的工具辅助编码.

3. 浅要剖析C++中的智能指针

C++中的智能指针包含在#include<memory>头文件中,主要有四种智能指针,

  1. auto_ptr(C++98,C++11之后不推荐使用)
  2. unique_ptr(C++11)
  3. shared_ptr(C++11)
  4. weak_ptr(C++11)
智能指针设计原理剖析

前面提到智能指针是基于RAII的思想来实现资源的自动释放,那么到底如何时现,下面提供一段简单代码可供参考

template <class T>
class smtPtr {
public:
	smtPtr(T* ptr = nullptr)
		:_ptr(ptr)
	{}
	~smtPtr() {
		if (_ptr)
			delete _ptr;
	}
	T* operator->() {
		return _ptr;
	}
	T& operator{
		return *_ptr;
	}
private:
	T* _ptr;
};

可以发现,这段代码中并没有提供拷贝构造,赋值运算符重载函数.
为什么这里没有这些函数, 这些问题后面会进行解释.

auto_ptr

auto_ptr是C++98中就已经出现的智能指针,但在现在的官方文档中已经明确标注,不建议使用,为什么呢,看看下面一小段代码

void test() {
	auto_ptr<int> ap1(new int(0));
	cout << *ap1 << endl;
	auto_ptr<int> ap2;
	ap2 = ap1;
	cout << *ap1 << endl;
}

这段代码中的ap1和ap2如果是我们常用的指针,运行这段代码没有任何问题,但如果是auto_ptr就会出错,为什么会出错呢,这里可以看到ap2调用了一次赋值运算符重载,我们知道,智能指针释放资源是采用了RAII的思想,根据类的作用域范围自动清理资源,而如果有两个智能指针同时指向同一块内存地址,那么智能指针就会释放两次空间,造成资源多次释放的问题.
但是这里的问题并不是资源多次释放的问题,而是解引用异常,在第二次解引用ap1是产生了解引用异常.这里是因为,在auto_ptr中拷贝构造和赋值运算符重载会对上一个智能指针进行清理,因为这种智能指针只能实现一对一的操作,如果多个智能指针指向同一块内存地址,就会产生上述多次释放内存空间的问题.
因为auto_ptr的局限性和易出错的性质,在C++11之后官方明确标注不建议使用.

unique_ptr

unique_ptr是在C++11中,新推出的一种智能指针,相对于auto_ptr来说,unique_ptr禁用了拷贝构造和赋值运算符重载
所以,下面这段代码就会产生编译错误,因为赋值运算符重载在这里是被禁用的

void test() {
	unique_ptr<int> up1(new int(0));
	cout << *up1 << endl;
	unique_ptr<int> up2;
	up2 = up1;
}

由上述来看,unique_ptr相较于auto_ptr来说,安全系数确实是提高了不少,但是局限性也更大了,所以,之后还有其他智能指针提供不同的解决方案.

shared_ptr

由词意就可以看出shared_ptr实现的是一种共享智能指针,这里的共享智能指针指的是多个智能指针,可以指向同一块内存空间,而且不会出现多次清理资源的问题,
这里如果将auto_ptr和unique_ptr的错误代码转换为shared_ptr类型的代码就完全可以执行.

shared_ptr的实现原理

shared_ptr如何能实现共享智能指针呢,这里其实是通过一个简单的计数原理实现shared_ptr,每当有新的智能指针被拷贝或赋值的时候,当前shared_ptr的计数器就会加一,每当调用析构函数时,shared_ptr的计数器就会减一,当计数器减到零是就会释放资源,
那么如何实现每块内存地址对应同一个计数器呢,
这里提供三种参考思路,可以自己尝试带入思考一下,

  1. 值计数器
  2. static静态值计数器
  3. 指针计数器

当然shared_ptr虽然解决了上述两个智能指针所存在的问题,但也因为计数器的原因引入了新的问题,那就是当多个智能指针循环指向时,就会造成计数器失效的问题,所以就引出了最后一个智能指针

weak_ptr

weak_ptr严格意义上来说并不算是一种智能指针,因为weak_ptr的析构函数不会去释放资源,weak_ptr严格意义上来说更可以说是一种协助shared_ptr解决计数器失效问题的工具,
weak_ptr 存在相互赋值,也可以由shared_ptr赋值,但不能直接由指针赋值,这是不允许的
例如:

void test() {
	shared_ptr<int> sp(new int(0));
	weak_ptr<int> wp1 = sp;
	weak_ptr<int> wp2 = wp1;

}

这段代码执行没有任何问题
但是,weak_ptr<int> wp3(new int(0));
这句代码就会产生编译错误.
同时也要注意,weak_ptr不能直接访问内存,要把weak_ptr转为shared_ptr才可以访问内存.

最后,提出一个问题,可以思考一下或者参考C++源码了解一下,智能指针如何实现线程安全.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值