偶尔在一本书上看到了称为智能指针的auto_ptr能自动释放没有释放的对象,刚开始很好奇,不知道是用什么原理,后面查了一下资料才发现,这个原理很简单,其实就是用到了在栈上创建的对象随之栈的释放自动调用析构函数的原理,在加上对c++函数模板的巧妙运用。以下例子希望能给读者带来帮助, 若有不对的地方欢迎指出。

动态分配资源的自动释放的英文是 Resource Allocation In Initialization,通常缩写成RAII。

根据《C++ Primer》第4版:

During stack unwinding, the function containing the throw, and possibly other functions in the call chain, are exited prematurely. In general, these functions will have created local objects that ordinarily would be destroyed when the function exited. When a function is exited due to an exception, the compiler guarantees that the local objects are properly destroyed. As each function exits, its local storage is freed. … If the local object is of class type, the destructor for this object is called automatically. As usual, the compiler does not work to destroy an object of built-in type.

假定有一个类定如下:

// 资源类
class Student
{
public:Student(const string name = "Andrew", string gender = "M", int age = 6){this->name = name;this->gender = gender;this->age = age;}void printStudentInfo(){cout << "Name: " << name << " ";cout << "Gender: " << gender << " ";cout << "Age: " << age << endl;//throw runtime_error("Throw an exception deliberately...");                          // (1)}~Student(){// 下面这条语句用来提示资源是否被释放了cout << "The destructor of class Student is called" << endl;}
private:string name;string gender;int age;
};

那么类似下面代码中的方式使用Student这个类,就会造成内存泄漏:

// 测试代码

int main()

{

Student *stu = new Student("Andrew", "M", 6);

stu->printStudentInfo();

return 0;

}

因为在return之前,没有delete掉stu这个动态分配而来的资源(如果仅仅是这个程序,也没有内存泄漏之忧,因为整个应用都结束运行了,在此只是为了方便说明类似的使用方式是不可以的,即用了new动态分配了资源,而没有对应的delete去回收)。为了防止这样的方式造成的内存泄漏,上面的测试代码应该增加一行delete stu:

// 测试代码

int main()

{

Student *stu = new Student("Andrew", "M", 6);                               // (2)

stu->printStudentInfo();

delete stu;                                                                                            // (3)

return 0;

}

这样就不会造成内存泄漏了。运行该应用,输出的结果是:

Name: Andrew Gender: M Age: 6

The destructor of class Student is called

输出的The destructor of class Student is called表明了Student类的析构函数得到了调用。

现在,如果在(2)和(3)中间发生了异常,即将Student类中的(1)语句uncomment掉,就会出现下面这样的错误提示:

This application has requested the Runtime to terminate it in an unusual way. Please contact the application’s support team for more information.

这样一来语句(3)即delete stu;就不会得到执行,因此也会造成内存泄漏。为了消除上面的错误,我们在前面的测试代码中,增加try-ctach来捕获异常,代码如下:

// 测试代码

int main()

{

Student *stu = new Student("Andrew", "M", 6);                              // (2)

try

{

stu->printStudentInfo();

}

catch(const exception &e)

{

cout << e.what() << endl;

}

delete stu;                                                                                                      // (3)

return 0;

}

输出结果:

Name: Andrew Gender: M Age: 6

Throw an exception deliberately…

The destructor of class Student is called

这就说明,如果增加了try-catch,后面的delete stu;就会将用new动态分配的资源正确的予以释放。

进一步地,如果在stu->printStudentInfo();中出现了异常,而且后面也没有delete stu;这样的语句,用new分配给stu的资源进行自动释放?办法是有的。为此,我们需要增加一个类,定义如下:

// 资源管理类
template<typename T>
class Resource
{
public:Resource(T* p){// 将新分配的到的资源的地址赋给res,现在res指向了新分配到的资源res = p;} ~Resource(){if(res){// 如果res指向的地址中,资源还没有被释放,那么则释放之delete res;res = 0;cout << "Resources are deallocated here." << endl;}}
private:T *res;
};

把测试代码改为:

// 测试代码

int main()

{

Student *stu = new Student("Andrew", "M", 6);                     // (2)

// 将stu绑定到Resource<Student>类型的res变量,res是一个local对象,当程序超出其可见范围时

// 其析构函数会自动执行。而它的析构函数中,又会自动释放stu所指向的资源

Resource<Student> res(stu);

try

{

stu->printStudentInfo();

}

catch(const runtime_error &e)

{

cout << e.what() << endl;

}

return 0;

}

上面代码的运行结果:

Name: Andrew Gender: M Age: 6

Throw an exception deliberately…

The destructor of class Student is called

Resources are de-allocated here.

这说明即使没有delete stu;程序也正确地析构了相关动态非配的资源,从而实现了动态分配资源的自动释放。

在上面的代码中,我们用stu->printStudentInfo();来调用相关的函数,如果想用res直接调用,则需要重载Resource类的->操作符,代码如下:

// 资源管理类
template<typename T>
class Resource
{
public:Resource(T* p){// 将新分配的到的资源的地址赋给res,现在res指向了新分配到的资源res = p;}~Resource(){if(res){// 如果res指向的地址中,资源还没有被释放,那么则释放之delete res;res = 0;cout << "Resources are deallocated here." << endl;}}T* operator->() const{if(res)return res;elsecout << "The underlying object is empty." << endl;}
private:T *res;
};

粗体字部分重载了->操作符,用来返回该类中的私有成员变量res。

相应的,测试代码做如下修改:

// 测试代码

int main()

{

Student *stu = new Student("Andrew", "M", 6);                     // (2)

// 将stu绑定到Resource<Student>类型的res变量,res是一个local对象,当程序超出其可见范围时

// 其析构函数会自动执行。而它的析构函数中,又会自动释放stu所指向的资源

Resource<Student> res(stu);

try

{

//stu->printStudentInfo();            // 这行被注释掉

res->printStudentInfo();            // 改用res来调用printStudentInfo

}

catch(const runtime_error &e)

{

cout << e.what() << endl;

}

return 0;

}

运行结果和前面是一致的,也实现了动态分配资源的自动释放。事实上,这就是大名鼎鼎的auto_ptr的实现原理。

注意,代码开始处需要包含以下语句:

#include <iostream>

#include <string>

#include <stdexcept>                 // 处理异常时,必须包含此头文件

using namespace std;

auto_ptr的缺陷

上面我实现的my_auto_ptr基本上是实现了auto_ptr的所有核心功能.从里面我们可以明显的看到一个很大缺陷.我们看到当通过复构造函数,通过操作符=赋值后,原来的那个智能指针对象就失效了.只有新的智能指针对象可以有效使用了.用个专业点的说法叫所有权的转移.被包装的指针指向的内存块就像是一份独享占用的财产,当通过复制构造,通过=赋值传给别的智能指针对象时,原有的对象就失去了对那块内存区域的拥有权.也就是说任何时候只能有一个智能指针对象指向那块内存区域,不能有两个对象同时指向那块内存区域.

这样一来auto_ptr不能做为STL中容器的元素,为啥呢? 因为容器中经常存在值拷贝的情况嘛,把一个容器对象直接赋值给另一对象.完了之后两个容器对象可得都能用啊.而如果是auto_ptr的话显然赋值后只能一个有用,另一个完全报废了.另外比如你有个变量auto_ptr<int> ap( new int(44) );  然后ap被放进一个容器后,ap就报废不能用了.

不过没办法啊,在c++ 11标准之前,现在我们大部分用的是98标准吧,STL里面只有auto_ptr这一种智能指针.而在11标准中除了auto_ptr还有如下三种:

unique_ptr

smart pointer with unique object ownership semantics

只能有一个主人的指针,可以用于STL容器

shared_ptr

smart pointer with shared object ownership semantics

可共享的指针

weak_ptr

weak reference to an object managed by std::shared_ptr

弱引用指针

c++ atuo_ptr 的实现原理相关推荐

  1. UUID的使用及其原理

    今天敲项目要用UUID,想起之前老师告诉UUID的使用,但没说具体的生成逻辑,于是我进行了百度 首先,UUID的使用: //生成随机的UUID String uuid = UUID.randomUUI ...

  2. etcd 笔记(01)— etcd 简介、特点、应用场景、常用术语、分布式 CAP 理论、分布式原理

    1. etcd 简介 etcd 官网定义: A highly-available key value store for shared configuration and service discov ...

  3. git原理及常见使用方法

    Git 原理入门-来自阮一峰 Git 是最流行的版本管理工具,也是程序员的必备技能之一. 即使天天使用它,很多人也未必了解它的原理.Git 为什么可以管理版本?git add.git commit这些 ...

  4. 微机原理—定时计数控制接口

    别看题目很高深,其实就是很简单的定时器和计数器而已. 通常用手机定个闹钟,就是定时器的使用. 工厂里通过传送带上安装传感器,传感器传输给计算机的信号用来计数. 这是一些很简单的应用,通过很小的一个芯片 ...

  5. 三层交换机原理:01路由器如何隔离广播域?

    前言: 当网络规模较大的时候,需要设备来隔离广播域,防止网络中因产生广播风暴而导致网络效率降低,而二层交换机不能隔离广播域,所以需要三层路由器设备来隔离广播域! 但三层路由器为什么能够隔离广播域,是如 ...

  6. CRF(条件随机场)与Viterbi(维特比)算法原理详解

    摘自:https://mp.weixin.qq.com/s/GXbFxlExDtjtQe-OPwfokA https://www.cnblogs.com/zhibei/p/9391014.html C ...

  7. BiLSTM-CRF学习笔记(原理和理解) 维特比

    https://www.zhihu.com/question/20136144 维特比详解 BiLSTM-CRF 被提出用于NER或者词性标注,效果比单纯的CRF或者lstm或者bilstm效果都要好 ...

  8. 【Learning Notes】线性链条件随机场(CRF)原理及实现

    1. 概述 条件随机场(Conditional Random Field, CRF)是概率图模型(Probabilistic Graphical Model)与区分性分类( Discriminativ ...

  9. Jieba分词原理与解析

    1 HMM模型 马尔科夫过程: 以天气判断为例:引出隐马尔科夫模型 于是我们可以将这种类型的过程建模为有一个隐藏的马尔科夫过程和一个与这个隐藏马尔科夫过程概率相关的并且可以观察到的状态集合.这就是本文 ...

  10. 自然语言处理课程(二):Jieba分词的原理及实例操作

    上节课,我们学习了自然语言处理课程(一):自然语言处理在网文改编市场的应用,了解了相关的基础理论.接下来,我们将要了解一些具体的.可操作的技术方法. 作为小说爱好者的你,是否有设想过通过一些计算机工具 ...

最新文章

  1. php checkbox美化,纯CSS3美化radio和checkbox_html/css_WEB-ITnose
  2. TableLayout(表格布局)
  3. 读《惰者集》有感:数学是一门需要敏锐感觉的学问
  4. python培训班哪些比较好-在线Python编程培训哪家机构比较好?
  5. ssh连接+执行系统命令
  6. 编译调试 .NET Core 5.0 Preview 并分析 Span 的实现原理
  7. java jdbc 删除_java使用jdbc实现各种类型添加删除修改数据
  8. 组合计数与反演 —— 反演
  9. C语言中宏定义和函数的取舍
  10. interface_cast做什么的?
  11. windows2012 wsus6 更新慢
  12. 如何用PHP和JavaScript比较时期(包括时间)
  13. 保定学院计算机编程,惠普HP打印机驱动程序安装失败怎么办hewlett-packard上的文件...
  14. 微信支付功能测试用例
  15. 人工智能-语音交互-NLP自然语言(三) 语法-敲黑板
  16. java开发深圳平均工资_深圳2017平均工资100173元!!这次你又又又拖后腿了吗!?...
  17. Python判断节假日 chinese_calendar
  18. 【计算机组成原理】学了计组,CPU执行时间你肯定会算了吧
  19. 【网络】抓包tcpdump
  20. h5 适配 iphoneX 的全面屏 (上有刘海 ,下有呼吸灯) ,在iphonX 上 fixed定位的问题

热门文章

  1. SpringBoot+VUE项目启动方式
  2. c语言三个学生每人四门,C语言一道题目,求教教3.统计一个班的学生成绩。要求程序具有如下功能:(1) 每个学生的学号和四门功课的成绩从键盘读入。...
  3. video视频播放中trank标签字幕显示问题
  4. 小米路由器4a千兆版修改sn和关闭电源led灯
  5. 使用wps的邮件合并和文档附件制作带照片的准考证
  6. 入侵手游服务器修改数据库,如何入侵手游服务器数据库
  7. pandas to_excel产生空值的解决方案
  8. Android 裁剪摄像头预览窗口-SurfaceView
  9. Isilon旧机器重新初始化
  10. w10系统桌面的计算机找不到,w10桌面我的电脑图标不见了怎么办