首先,我要声明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的更多相关文章

  1. 【C++】智能指针详解(一):智能指针的引入

    智能指针是C++中一种利用RAII机制(后面解释),通过对象来管理指针的一种方式. 在C++中,动态开辟的内存需要我们自己去维护,在出函数作用域或程序异常退出之前,我们必须手动释放掉它,否则的话就会引 ...

  2. [转]C++ 智能指针详解

    转自:http://blog.csdn.net/xt_xiaotian/article/details/5714477 C++ 智能指针详解 一.简介 由于 C++ 语言没有自动内存回收机制,程序员每 ...

  3. C++ 智能指针详解(转)

    C++ 智能指针详解   一.简介 由于 C++ 语言没有自动内存回收机制,程序员每次 new 出来的内存都要手动 delete.程序员忘记 delete,流程太复杂,最终导致没有 delete,异常 ...

  4. 【C++】智能指针详解

    转自:https://blog.csdn.net/flowing_wind/article/details/81301001 参考资料:<C++ Primer中文版 第五版>我们知道除了静 ...

  5. C++智能指针详解

    本文出自http://mxdxm.iteye.com/ 一.简介 由于 C++ 语言没有自动内存回收机制,程序员每次 new 出来的内存都要手动 delete.程序员忘记 delete,流程太复杂,最 ...

  6. 【转】C++ 智能指针详解

    一.简介 由于 C++ 语言没有自动内存回收机制,程序员每次 new 出来的内存都要手动 delete.程序员忘记 delete,流程太复杂,最终导致没有 delete,异常导致程序过早退出,没有执行 ...

  7. [C++11新特性] 智能指针详解

    动态内存的使用很容易出问题,因为确保在正确的时间释放内存是极为困难的.有时我们会忘记释放内存产生内存泄漏,有时提前释放了内存,再使用指针去引用内存就会报错. 为了更容易(同时也更安全)地使用动态内存, ...

  8. C++11 unique_ptr智能指针详解

    在<C++11 shared_ptr智能指针>的基础上,本节继续讲解 C++11 标准提供的另一种智能指针,即 unique_ptr 智能指针. 作为智能指针的一种,unique_ptr ...

  9. c/c++指针详解(一)

    一:相关概念 1.指针数组:int *p[6]               是数组,是一个存放指针的数组,也就是里面存放的是地址. 2.数组指针:int (*p)[6]                 ...

随机推荐

  1. nodejs笔记2 --关于nodejs最新启动方式

    1,运行应用以前是node app.js或者 supervisor app.js 2, 现在的应用方式是npm start或者supervisor bin/www

  2. Qt之hello world

    本人使用的是Qt5.7版本的,请读者自主下载安装. 今天首先来进行Qt入门的第一个程序,也是很经典的一个例子.这是在很多的变成语言中都会用到的例子,就是输出helloworld这个信息.Qt中使用的变 ...

  3. JavaScript中的this关键字的用法和注意点

    JavaScript中的this关键字的用法和注意点 一.this关键字的用法 this一般用于指向对象(绑定对象); 01.在普通函数调用中,其内部的this指向全局对象(window); func ...

  4. java中浅层克隆和深层克隆

    1.浅复制与深复制概念 浅复制(浅克隆)     被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象 ...

  5. JavaScript中国象棋程序(7) - 置换表

    "JavaScript中国象棋程序" 这一系列教程将带你从头使用JavaScript编写一个中国象棋程序.这是教程的第2节. 这一系列共有9个部分: 0.JavaScript中国象 ...

  6. Extjs 数据代理

    Ext.data.proxy.Proxy 代理类的根类 客户端代理: 1.LocalStorageProxy:将数据存储在localStorage中,此种方式可以持久的将数据存储在客户端 要使用代理, ...

  7. 使用curl上传报错问题排查

    1. THE STOR COMMAND 说明存储出了问题,处理方案: 方案1: 请检查ftp服务器存储是否已满,若已满则清理一下空间即可. 方案2: 若ftp服务器存储未满,请检查是否有上传了的文件, ...

  8. 用react分页显示数据

    去年年底,尝试着用react写个组件化的页面! demo地址 里面有一个list页面弄了一下数据的分页展示 展示一下主要三个组件:父组件listBox.列表组件List.按钮组件PageButton ...

  9. 关于volatile的可见性和禁止指令重排序的疑惑

    在学习volatile语义的可见性和禁止指令重排序的相关测试中,发现并不能体现出禁止指令重排序的特性 实验代码如下 package com.aaron.beginner.multithread.vol ...

  10. 了解 : 多个Http请求设计方向 (batch)

    之前都是一个restful的请求,每次只能ajax一个资源,但是遇到比较多个请求时,都是用RPC来完成,但是却让后台开了许多接口,代码开始不整齐!当然roll back只能交给RPC来负责. 游览器没 ...