由于c++没有垃圾回收机制,像堆只能手动开辟内存,手动释放,像栈只能系统开辟,系统释放,于是智能指针出现了,它实现了内存的手动开辟,系统释放,防止了内存泄漏问题;

我们知道, 栈对象在离开其作用域的时候, 会自动调用析构函数, 所以, 可以考虑把某一栈对象与某一堆内存绑定,且在其析构函数中释放堆内存,  那么, 在该栈对象离开作用域时, 堆内存自动释放, 这就是智能指针(本质是栈对象)的原理。  这个栈对象装得像指针一样, 所以我们称之为智能指针, 其实, 它不过就是个普通的栈对象而已。

在c++ 98 中只有auto-ptr,这个指针是不完善的,现在几乎都要被摒弃了,但我们还是要去了解一下他的思想,明白为何要被摒弃的呢?以及在c++11上又是如何对他如何改进的呢?

//自动指针即auto_ptr,不同于scoped_ptr指针的是自动指针会转移使用权
//在进行赋值或者拷贝构造之后,原来的auto_ptr会失去对所管指针的拥有权,并且将自己的指针赋为NULL
//同时,在赋值和拷贝构造之后,原来的auto_ptr的指针会指向NULL,也是它最大的弊端之一;

#include <iostream>
using namespace std;

// 简单类
class A
{
public:
void fun()
{

}
};

template<class T>

// 类模板
class auto_ptr
{
public:

// explicit构造函数, 禁止类型转化
explicit auto_ptr(T *p = 0) throw()
: m_bIsOwner(p != 0), m_ptr(p)
{
cout << "debug1" << endl;
}

// owner转移
auto_ptr(const auto_ptr<T>& y) throw()
: m_bIsOwner(y.m_bIsOwner), m_ptr(y.release())
{
cout << "debug2" << endl;
}

// owner转移
auto_ptr<T>& operator=(const auto_ptr<T>& y) throw()
{
cout << "debug3" << endl;

if (this != &y) // 当前对象不是y对象
{
cout << "debug4" << endl;

if (m_ptr != y.get()) // 当前对象绑定的地址不是y对象绑定的地址
{
cout << "debug5" << endl;

if (m_bIsOwner) // 如果当前对象已经绑定堆, 则要先释放
{
cout << "debug6" << endl;
delete m_ptr;
}

cout << "debug7" << endl;

m_bIsOwner = y.m_bIsOwner; // 转移owner
}
else if (y.m_bIsOwner) // 当前对象与y绑定到同一块堆上, 且y是owner, 则把y的owner转移给当前对象
{
cout << "debug8" << endl;

m_bIsOwner = true;
}

cout << "debug9" << endl;

m_ptr = y.release(); // 让y不再是owner
}

cout << "debug10" << endl;

return *this; // 返回当前对象的引用
}

// 析构函数
~auto_ptr()
{
cout << "debug11" << endl;

if (m_bIsOwner) // 只有拥有owner属性才释放堆, 这样避免重复释放
{
cout << "debug12" << endl;

delete m_ptr; // 即使m_ptr是空指针也木有关系
}
}

// 重载对象的*运算符, 使得对象"看起来"像指针, 可以执行*p操作
T& operator*() const throw()
{
cout << "debug13" << endl;

return *get();
}

// 重载对象的->运算符
T *operator->() const throw()
{
cout << "debug14" << endl;

return get();
}

// 获得对象绑定的地址
T *get() const throw()
{
cout << "debug15" << endl;

return m_ptr;
}

// 去掉对象的owner属性
T *release() const throw()
{
cout << "debug16" << endl;

((auto_ptr<T> *)this)->m_bIsOwner = false;
return m_ptr;
}

private:
bool m_bIsOwner; // 对象是否拥有为owner的标志
T *m_ptr; // 对象绑定的指针
};

int main()
{
{
cout << "------------------------------" << endl;

// 用法错误, 因为构造函数中有explicit, 不允许类型转化
//auto_ptr<int> p = new int(10);
}

{
cout << "------------------------------" << endl;

// ok
auto_ptr<int> p(new int(10));
}

{
cout << "------------------------------" << endl;

// 下面代码有严重的运行期错误, 实际上是尝试delete栈上的内容
int a = 10;
//auto_ptr<int> p(&a);
}

{
cout << "------------------------------" << endl;

auto_ptr<int> p(new int(10));

// 错误, p虽然"看似像"指针, 其本质是对象, delete p;是未定义行为
//delete p;
}

{
cout << "------------------------------" << endl;

int *q = new int(10);
auto_ptr<int> p(q);

// 错误, q释放一次, p释放一次, 重复释放啊
//delete q;
}

{
cout << "------------------------------" << endl;

auto_ptr<int> p0;

// 有debug3的打印, 但没有debug4, 知道原因了吧
p0 = p0;
}

{
cout << "------------------------------" << endl;

auto_ptr<int> p0(new int(10));

// 注意, 这是初始化, 不是复制, 所以不会有debug3的打印
auto_ptr<int> p1 = p0;
}

{
cout << "------------------------------" << endl;

auto_ptr<int> p0(new int(10));
auto_ptr<int> p1;

// 注意, 这才是赋值, 所有有debug3, debug4, debug5, debug7, debug9, debug10的打印
// 为什么没有debug6呢? 因为当前对象p1还不是owner
p1 = p0;
}

{
cout << "------------------------------" << endl;

auto_ptr<int> p0(new int(10));
auto_ptr<int> p1(new int(20));

// 有debug6的打印, 因为当先释放p1绑定的对象, 否则内存又泄露了啊
p1 = p0;
}

{
cout << "------------------------------" << endl;

auto_ptr<int> p0(new int(10));

// 把owner转给p1
auto_ptr<int> p1(p0);

// 终于见到你了, debug8
p0 = p1;
}

{
cout << "------------------------------" << endl;

auto_ptr<int> p(new int(10));

// 见到你了, debug13
cout << *p << endl;
}

{
cout << "------------------------------" << endl;

auto_ptr<A> p(new A());

// 终于见到你了, debug15
p->fun();
}

{
cout << "------------------------------" << endl;

auto_ptr<int> p0(new int(10));
auto_ptr<int> p1(p0);
auto_ptr<int> p2(p1);

// 实际上, p3才是最后的winner, 才是最后的owner, 所以释放堆的重任在p3身上
auto_ptr<int> p3(p2);
}

{
cout << "------------------------------" << endl;

// oh, my god, 内存泄露, 本来要delete [] q; 现在析构函数只执行delete q;
int *q = new int[3];
auto_ptr<int> p(q);
}

{
cout << "------------------------------" << endl;

// oh, my god, 内存泄露, 本来要delete [] q; 现在析构函数只执行delete q;
int *q = new int[3];
auto_ptr<int> p(q);

// 已经说过, 下面语句会造成内存重复释放
//delete q;
}

// 最后说明一下, auto_ptr不适合做容器的元素, 这一点我们以后会再次讨论到

return 0;

}

原文链接:https://blog.csdn.net/stpeace/article/details/45155487

测试用例及其好的一篇博客,思路很清晰

转载于:https://www.cnblogs.com/xcb-1024day/p/11331117.html

智能指针之atuo_ptr源码剖析相关推荐

  1. C++ 智能指针最佳实践源码分析

    作者:lucasfan,腾讯 IEG Global Pub.Tech. 客户端工程师 智能指针在 C++11 标准中被引入真正标准库(C++98 中引入的 auto_ptr 存在较多问题),但目前很多 ...

  2. boost源码剖析之:泛型函数指针类boost::function(rev#3)

    boost源码剖析之:泛型函数指针类boost::function(rev#3) 刘未鹏 C++的罗浮宫(http://blog.csdn.net/pongba)   Note: 并非新作,03年曾放 ...

  3. boost源码剖析之:泛型指针类any之海纳百川(rev#2)

    boost源码剖析之:泛型指针类any之海纳百川(rev#2) 刘未鹏 C++的罗浮宫(http://blog.csdn.net/pongba) 动机 C++是强类型语言,所有强类型语言对类型的要求都 ...

  4. boost源码剖析之:多重回调机制signal(上)

    boost源码剖析之:多重回调机制signal(上) 刘未鹏 C++的罗浮宫(http://blog.csdn.net/pongba) boost库固然是技术的宝库,却更是思想的宝库.大多数程序员都知 ...

  5. STL源码剖析 算法开篇

    STL源码剖析 算法章节 算法总览_CHYabc123456hh的博客-CSDN博客 质变算法 质变算法 - 会改变操作对象的数值,比如互换.替换.填写.删除.排列组合.分隔.随机重排.排序等 #in ...

  6. 《STL源码剖析》相关面试题总结

    一.STL简介 STL提供六大组件,彼此可以组合套用: 容器 容器就是各种数据结构,我就不多说,看看下面这张图回忆一下就好了,从实现角度看,STL容器是一种class template. 算法 各种常 ...

  7. STL源码剖析(一)STL简介

    STL源码剖析(一)STL简介 文章目录 STL源码剖析(一)STL简介 一.STL概述 二.STL六大组件 2.1 容器(containers) 2.2 算法(algorithms) 2.3 迭代器 ...

  8. STL源码剖析之配接器

    adapter(配接器)在STL组件的灵活组合运用上,扮演者转换器的角色.adapter来源于一种适配器模式,其功能是:将一个class接口转换为另一个class的接口,使得原本因接口不兼容而不能合作 ...

  9. Apollo 7.0——percception:lidar源码剖析(万字长文)

    文章目录 组件启动 实现组件类 实现组件头文件 实现组件源文件 设置配置文件 启动组件 激光感知 目录结构 源码剖析 detection--init InitAlgorithmPlugin detec ...

  10. 【C++标准库】std::string用法指南源码剖析

    文章目录 1.ASCII码 (1)计算机如何表达字符 2.C 语言中的字符类型 char (1)思想:char 即整数 (3)C 语言帮手函数 (4)C语言中的字符串 (4)C 语言转义符 3.C++ ...

最新文章

  1. java实现fread_fread函数读取到的数据和实际数据不一样
  2. python爬虫技术可以干什么-Python除了爬虫,还能干啥?
  3. PowerShell删除Exchange 2010邮件的正确姿势
  4. html中空格字符实体整理
  5. NavReady的使用
  6. hadoop2.7 伪分布
  7. 【CV101大会录播】人流密度/河道污染/安全帽识别获奖算法
  8. Oracle 查找并删除表中的重复记录
  9. html5游戏开发实战第三章,我的启蒙--HTML5 第三章 Canvas
  10. 优化MyBatis配置文件中的配置
  11. android 底边框_Android底表
  12. Webpack学习大纲
  13. 凤凰os 停在android,电脑的安卓系统,凤凰OS:有亮点但还很粗糙的系统
  14. C语言学习 ISBN码
  15. linux下文件属性drwxr-xr-x各是什么意思
  16. Mask to Polygons mask转Polygons并保存为labelMe json文件
  17. Linux mail命令使用和sendmail配置
  18. 列表最后一页的最后一条数据删除之后,页码自动减一
  19. uni-app点击按钮弹出提示框-uni.showModal(OBJECT),选择确定和取消
  20. 孙宇晨:“这就像1932年的毕加索”

热门文章

  1. android往mac投屏,安卓手机怎么投屏到Mac系统中?
  2. SpringBoot整合MyBatis(七)
  3. python平方根_如何在Python中找到平方根?
  4. 小波变换matlab程序,图像小波变换原理_图像小波变换的matlab实现详解
  5. UDP如何实现可靠性传输
  6. python数据可视化matplotlib之grid
  7. Word设置封面无页码,摘要罗马数字页码,正文数字页码
  8. 数据库删除数据 truncate 与 delete
  9. OpenGL ES着色器语言----------------储存修饰符
  10. html excel零不显示,Excel中把0显示为空白的三种解决方法