【C++】智能指针详解(二):auto_ptr
首先,我要声明auto_ptr是一个坑!auto_ptr是一个坑!auto_ptr是一个坑!重要的事情说三遍!!!
通过上文,我们知道智能指针通过对象管理指针,在构造对象时完成资源的分配及初始化,在析构对象时完成资源的清理及汕尾工作.
因此,可以得到一份简洁版的智能指针代码:
template<typename T>
class AutoPtr{
public:
//构造函数,完成资源的初始化与分配
AutoPtr(T * ptr = NULL)
:_ptr(ptr){}
//析构函数,完成资源的清理及汕尾工作
~AutoPtr(){
if(_ptr!=NULL){
delete _ptr;
_ptr = NULL;
}
}
private:
T *_ptr;
};
大致一看,没毛病!突然觉得自己无所不能,感觉自己就是传说中的编程天才!
可是,如果我想这样的话.....:
AutoPtr<int> ap1(new int(100));
AutoPtr<int> ap2(new int(200));
AutoPtr<int> ap3(ap1);
AutoPtr<int> ap4();
ap4 = ap2;
ap1与ap3共同管理一块空间;ap2与ap4共同管理一块空间,看起来好像没什么问题.
但当程序跑起来,出了函数作用域之后....崩毁了!!?
......Why?
好在我经验丰富,见多识广,脑袋回路中很自然地想起了类似的情况:string类的浅拷贝....
因此,机智的我立刻发现了原因:由于没有定义拷贝构造函数与赋值运算符重载,那么在拷贝构造对象和给对象赋值时,系统会默认生成相应函数.
ap1与ap3共同管理一块空间,一旦出了函数作用域,ap3会调用析构函数,delete掉所指向的空间;
而当ap1调用析构函数时,此时ap1所指向的已经是一块非法内存(因为被ap3 delete过了),因此当ap1再次delete这块空间时,程序挂掉了!
简而言之:同样一块空间被delete了两次,所以最终程序挂掉了!
那么我就好奇了,auto_ptr如何应对拷贝与赋值的呢?
在百度了各种资料及阅读其源代码之后,发现auto_ptr是这么处理的:
//拷贝构造
AutoPtr(AutoPtr& ap){
//转移管理权
_ptr = ap._ptr;
ap._ptr = NULL;
}
//赋值运算符重载
AutoPtr &operator=(AutoPtr &ap){
if(ap._ptr != _ptr){
AutoPtr tmp(ap);
std::swap(_ptr,tmp._ptr);
}//由析构函数去管理tmp
return *this;
}
这是我简化后的代码,再次应对上述情况时:
AutoPtr<int> ap1(new int(100));
AutoPtr<int> ap2(new int(200));
AutoPtr<int> ap3(ap1); //ap3 = NULL
AutoPtr<int> ap4();
ap4 = ap2; //ap2 = NULL
我们发现:auto_ptr通过转移管理权,来保证在赋值与拷贝时仅管理一份指针,而防止同一块空间释放多次的问题.
最后,我将自己写的简洁、精简、易读的AutoPtr与库中的代码一起贴上来
/*
*文件说明:智能指针之AutoPtr
*作者:高小调
*日期:2017-03-30
*集成开发环境:Microsoft Visual Studio 2010
*Github:https://github.com/gaoxiaodiao/c_cplusplus/blob/master/SmartPointer/AutoPtr.h
*/
#pragma once
template<typename T>
class AutoPtr{
public:
//构造函数
AutoPtr(T * ptr = NULL)
:_ptr(ptr){}
//拷贝构造
AutoPtr(AutoPtr& ap){
//转移管理权
_ptr = ap._ptr;
ap._ptr = NULL;
}
//赋值运算符重载
AutoPtr &operator=(AutoPtr &ap){
if(ap._ptr != _ptr){
AutoPtr tmp(ap);
std::swap(_ptr,tmp._ptr);
}//由析构函数去管理tmp
return *this;
}
//析构函数
~AutoPtr(){
if(_ptr!=NULL){
delete _ptr;
_ptr = NULL;
}
}
private:
T *_ptr;
}; void TestAutoPtr(){
AutoPtr<int> ap1(new int(100));
AutoPtr<int> ap2(new int(200));
AutoPtr<int> ap3(ap1);
AutoPtr<int> ap4(ap2);
ap3 = ap4;
}
库内实现(我就懒得写注释了,看完精简版后,再看库中实现会发现库内的封装性、代码复用性更高一些)
template<class T>
class auto_ptr
{
private:
T*ap;
public:
//constructor & destructor-----------------------------------(1)
explicit auto_ptr(T*ptr=0)throw():ap(ptr)
{
} ~auto_ptr()throw()
{
delete ap;
}
//Copy & assignment--------------------------------------------(2)
auto_ptr(auto_ptr& rhs)throw():ap(rhs.release())
{
}
template<class Y>
auto_ptr(auto_ptr<Y>&rhs)throw():ap(rhs.release())
{
}
auto_ptr& operator=(auto_ptr&rhs)throw()
{
reset(rhs.release());
return *this;
}
template<class Y>
auto_ptr& operator=(auto_ptr<Y>&rhs)throw()
{
reset(rhs.release());
return *this;
}
//Dereference----------------------------------------------------(3)
T& operator*()const throw()
{
return *ap;
}
T* operator->()const throw()
{
return ap;
}
//Helper functions------------------------------------------------(4)
//value access
T* get()const throw()
{
return ap;
}
//release owner ship
T* release()throw()
{
T*tmp(ap);
ap=0;
return tmp;
}
//reset value
void reset(T*ptr=0)throw()
{
if(ap!=ptr)
{
deleteap;
ap=ptr;
}
}
//Special conversions-----------------------------------------------(5)
template<class Y>
struct auto_ptr_ref
{
Y*yp;
auto_ptr_ref(Y*rhs):yp(rhs){}
};
auto_ptr(auto_ptr_ref<T>rhs)throw():ap(rhs.yp)
{
} auto_ptr& operator=(auto_ptr_ref<T>rhs)throw()
{
reset(rhs.yp);
return*this;
} template<class Y>
operator auto_ptr_ref<Y>()throw()
{
returnauto_ptr_ref<Y>(release());
} template<class Y>
operator auto_ptr<Y>()throw()
{
returnauto_ptr<Y>(release());
}
};
与君共勉!
【C++】智能指针详解(二):auto_ptr的更多相关文章
- 【C++】智能指针详解(一):智能指针的引入
智能指针是C++中一种利用RAII机制(后面解释),通过对象来管理指针的一种方式. 在C++中,动态开辟的内存需要我们自己去维护,在出函数作用域或程序异常退出之前,我们必须手动释放掉它,否则的话就会引 ...
- [转]C++ 智能指针详解
转自:http://blog.csdn.net/xt_xiaotian/article/details/5714477 C++ 智能指针详解 一.简介 由于 C++ 语言没有自动内存回收机制,程序员每 ...
- C++ 智能指针详解(转)
C++ 智能指针详解 一.简介 由于 C++ 语言没有自动内存回收机制,程序员每次 new 出来的内存都要手动 delete.程序员忘记 delete,流程太复杂,最终导致没有 delete,异常 ...
- 【C++】智能指针详解
转自:https://blog.csdn.net/flowing_wind/article/details/81301001 参考资料:<C++ Primer中文版 第五版>我们知道除了静 ...
- C++智能指针详解
本文出自http://mxdxm.iteye.com/ 一.简介 由于 C++ 语言没有自动内存回收机制,程序员每次 new 出来的内存都要手动 delete.程序员忘记 delete,流程太复杂,最 ...
- 【转】C++ 智能指针详解
一.简介 由于 C++ 语言没有自动内存回收机制,程序员每次 new 出来的内存都要手动 delete.程序员忘记 delete,流程太复杂,最终导致没有 delete,异常导致程序过早退出,没有执行 ...
- [C++11新特性] 智能指针详解
动态内存的使用很容易出问题,因为确保在正确的时间释放内存是极为困难的.有时我们会忘记释放内存产生内存泄漏,有时提前释放了内存,再使用指针去引用内存就会报错. 为了更容易(同时也更安全)地使用动态内存, ...
- C++11 unique_ptr智能指针详解
在<C++11 shared_ptr智能指针>的基础上,本节继续讲解 C++11 标准提供的另一种智能指针,即 unique_ptr 智能指针. 作为智能指针的一种,unique_ptr ...
- c/c++指针详解(一)
一:相关概念 1.指针数组:int *p[6] 是数组,是一个存放指针的数组,也就是里面存放的是地址. 2.数组指针:int (*p)[6] ...
随机推荐
- Vuex 模块化与项目实例 (2.0)
Vuex 强调使用单一状态树,即在一个项目里只有一个 store,这个 store 集中管理了项目中所有的数据以及对数据的操作行为.但是这样带来的问题是 store 可能会非常臃肿庞大不易维护,所以就 ...
- 解决CSS中float:left后需要clear:both清空
现在,大部分的横排导航都是通过 ul -> li *n -> a 来实现的.具我所知,要达到这种效果,有几种方法可以实现. 1.传统处理方式: li {float:left;}/*这样,对 ...
- RDLC系列(一)ASP.NET RDLC 报表自定义数据源
最近一段时间开发ERP系统中要用到不少报表打印,在网上找了一圈发现想些好用的报表控件大部分要收费,一些面免费要么不好用要么IE8不兼容,最后还是用了微软自带的RDLC报表,把自己遇到的坑和技巧整理分享 ...
- Sicily 1151 魔板
Constraints Time Limit: 1 secs, Memory Limit: 32 MB , Special Judge Description 魔板由8个大小相同方块组成,分别用涂上不 ...
- 每天一个linux命令(34)--top命令
今天给领导发邮件,我这边虽然显示发出去了,但是他那边一直没收到,结果我以为我发了,他又一直在那边等结果.所以说,以后要另外发个信息或者QQ微信之类的说一声. top命令是Linux 下常用的性能分析工 ...
- STL_deque双端队列
deque:元素数据采用分块的线性结构存储.若干线性存储块成为deque块.一般大小为512字节,元素的数据类型所占用的字节数,决定了每个deque块可容纳的元素个数. 所有的deque块使用一个Ma ...
- c#基础语句——循环语句(for、while、foreach)
循环类型:for.while.foreach 循环四要素:初始条件-->循环条件-->循环体-->状态改变 1.for 格式: for(初始条件:循环条件:状态改变) {循环体(br ...
- 关于Android App开发技术分类的一个小总结
前言 本文从热更新.异步并发.性能优化.网络请求等多个方面对Android App开发的技术进行了一个分类总结.欢迎大家沟通交流. 热更新 [原]热更新开源项目Tinker源码解析之Dex热更新 [ ...
- 2017-3-10 SQL server 数据库 T--SQL语句
创建数据库:create datebase 数据库名 注:数据库名不能为中文,不能数字开头,不能符号开头. 删除数据库:drop datebase 数据库名 创建表:create tab ...
- ActiveMQ学习系列(四)----消息持久化到mysql
前记:目前学习还比较杂乱,还未找到系统化地学习ActiveMq的方法.在网上看到消息持久化的demo,了解了一下,在此记录. 一.目前ActiveMq支持的持久化方法 url:http://activ ...