第五章 RALL机制与智能指针

  • RAII机制
  • auto_ptr资源所有权转移智能指针
  • 独占智能指针unique_ptr
  • shared_ptr共享智能指针
  • weak_ptr破环指针
  • 智能指针的问题与解决方案:删除器(Deletor)

RAII机制

RAII 是 resource acquisition is initialization 的缩写,意为“资源获取即初始化”。它是 C++ 之父 Bjarne Stroustrup 提出的设计理念,其核心是把资源和对象的生命周期绑定,对象创建获取资源,对象销毁释放资源。在 RAII 的指导下,C++ 把底层的资源管理问题提升到了对象生命周期管理的更高层次。

当我们 new 出一块内存空间,在使用完之后,如果不使用 delete 来释放这块资源则将导致内存泄露,这在中大型项目中是极具破坏性的。但是人无完人,我们并不能保证每次都记得释放无法再次获取到且不再使用的内存。

为了避免内存申请而由于人为的失误而出现的内存泄漏的问题,C++11把原来Boost库中非常好用的四根智能指针正式转正进入了C++11的标准库中。

C++11给我们程序员提供了四根智能指针,为了帮助程序员们在大中型项目做到资源的自动释放,保证中大型项目的安全,提供了以下四根智能指针:

智能指针并非是一个指针而是一个实现指针功能的类对象

智能指针对象是一个定在栈上的类对象:

他是一个类对象,实现了一个指针功能。意义就是这个栈上类对象托管一块堆上的资源。

智能指针的作用

所以这类对象在构造时,托管一块堆上的资源,在析构时,释放堆上的资源。

所以这个智能指针对象所拥有的特性就是栈上对象出栈时,自动销毁。

auto_ptr资源所有权转移智能指针

简介

auto_ptr 是通过由 new 表达式获得的对象,并在 auto_ptr 自身被销毁时删除该对象的智能指针。它可用于为动态分配的对象提供异常安全、传递动态分配对象的所有权给函数和从函数返回动态分配的对象。

复制 auto_ptr ,会复制指针并转移所有权给目标: auto_ptr 的复制构造和复制赋值都会修改其右侧参数,而且“副本”不等于原值。因为这些不常见的复制语义,不可将 auto_ptr 置于标准容器中。

封装能够实现指针功能的类对象源码

#include <iostream>
#include <memory>using namespace std;class Stu
{
private:int _age;string _name;
public:Stu(int age,string name){this->_age=age;this->_name=name;cout << "Stu structure" << endl;}void showInfo(){cout << "姓名:" << this->_name << ",年龄:" << this->_age << endl;}~Stu(){cout << "Stu destruct" << endl;}
};template<class T>
class Auto_ptr
{
private:T* _ptr;
public:Auto_ptr(T* ptr=nullptr){this->_ptr=ptr;cout << "Auto_ptr structure" << endl;}Auto_ptr(Auto_ptr& other){this->_ptr=other._ptr;other._ptr=nullptr;cout << "Auto_ptr copy_structure" << endl;}Auto_ptr& operator=(Auto_ptr& other){cout << "Auto_ptr operator= function" << endl;if(this==&other){return *this;}if(nullptr!=other._ptr){delete this->_ptr;this->_ptr=other._ptr;other._ptr=nullptr;}}T* operator->(){cout << "Auto_ptr operator-> function" << endl;return this->_ptr;}T* get_ptr(){cout << "Auto_ptr get_ptr_function " << endl;return this->_ptr;}~Auto_ptr(){if(nullptr!=this->_ptr)delete this->_ptr;cout << "Auto_ptr destruct" << endl;}
};int main()
{//在堆上开辟对象空间(需要手动释放)Stu* stu=new Stu(19,"Tom");stu->showInfo();delete stu;//通过指针模板类,创建对象,用于验证get_ptr()函数Auto_ptr<Stu> s1(new Stu(20,"DaMing"));//通过get_ptr函数获取属性指针,访问抽象类对象中的成员函数s1.get_ptr()->showInfo();//指针模板类的拷贝构造,此刻s1所有资源转移Auto_ptr<Stu> s2=s1;//指针模板类中的->运算符重载函数s2->showInfo();//通过指针模板类,创建对象Auto_ptr<Stu> s3(new Stu(15,"LiLei"));s3->showInfo();//指针模板类中的->运算符重载函数s3=s2;//指针模板类中的=运算符重载函数,此刻s2所有资源转移s3->showInfo();//指针模板类中的->运算符重载函数//调用头文件#include<memory>下的//auto_ptr智能指针(在c++17弃用)auto_ptr<Stu> s4(new Stu(18,"XiaoMei"));s4->showInfo();return 0;
}

运行结果分析图

独占智能指针unique_ptr

简介

unique_ptr智能指针,一个unique_ptr指针只管理一个堆上资源,不发生拷贝构造,也不会等号运算符重载。

unique,英译:独一无二的,唯一的智能指针,他是不共享的,他不像其它的共享对象,也操作他的指向的堆区空间,他是唯一一个可操作这个堆区空间的对象。也就是说他的类对象是不可以发生拷贝构造与赋值运算符重载的。

简单使用方法

#include <iostream>
#include <memory>using namespace std;class Stu
{
private:int _age;string _name;
public:Stu(int age,string name){this->_age=age;this->_name=name;cout << "Stu structure" << endl;}void showInfo(){cout << "姓名:" << this->_name << ",年龄:" << this->_age << endl;}~Stu(){cout << "Stu destruct" << endl;}
};int main()
{unique_ptr<Stu> u1(new Stu(19,"DaMing"));u1.get()->showInfo();return 0;
}

运行结果分析图

问题

如何实现内部实现?

  1. 把这个拷贝构造与赋值运算符函数设为私有。
  2. 删除类对象中的成员函数。 //系统给我提供了一个关键字可以把他给删除了。在函数名后面加上 = delete;
unique_ptr(const unique_ptr& ptr) =  delete;
void operator=(unique_ptr& ptr) = delete;

shared_ptr共享智能指针

简介

shared_ptr共享智能指针是可以由多个栈上的智能指针对象同时托管一块堆资源

shared_ptr内部实现:堆上引用计数器,计数器个数为0时,开始释放内存空间

#include <iostream>
#include <memory>
using namespace std;class Stu{
private:string name;int age;
public:Stu(string name,int age){this->age=age;this->name=name;}void showInfo(){cout << "姓名:" << this->name << ",年龄:" << this->age << endl;}~Stu(){cout << "stu destruct" << endl;}
};//实现一个Shared_ptr的引用计数
template <class T>
class RefCount{
private:T* ptr;int count;
public:RefCount(T* ptr=nullptr){if(ptr!=nullptr){this->count=1;}else{this->count=0;}}//增加引用计数void addRef(){this->count++;}//减少引用计数int delRef(){return --count;}int get_count(){return this->count;}
};template <class T>
class Shared_ptr{
private:T* ptr;RefCount<T>* refcount;
public://构造函数Shared_ptr(T* ptr=nullptr){this->ptr=ptr;if(this->ptr!=nullptr){this->refcount=new RefCount<T>(ptr);}else {this->refcount=new RefCount<T>();}}//析构函数~Shared_ptr(){if(refcount->delRef()==0){delete this->ptr;this->ptr=nullptr;refcount=nullptr;}}//拷贝构造Shared_ptr(const Shared_ptr& other){if(other.ptr!=nullptr){this->ptr=other.ptr;this->refcount=other.refcount;refcount->addRef();}else {this->ptr=other.ptr;this->refcount=other.refcount;}}//等号运算符重载Shared_ptr& operator=(const Shared_ptr& other){if(this==&other){return this;}if(other.ptr!=nullptr){if(refcount->delRef()==0){delete this->ptr;delete refcount;this->ptr=nullptr;refcount=nullptr;}this->ptr=other.ptr;this->refcount=other.refcount;refcount->addRef();}else{if(refcount->delRef()==0){delete this->ptr;delete refcount;this->ptr=nullptr;refcount=nullptr;}this->ptr=other.ptr;this->refcount=other.refcount;}}T& operator*(){return *ptr;}T* operator->(){return ptr;}T* get(){return ptr;}int use_count(){return refcount->get_count();}
};int main()
{Stu *pstu=new Stu("yao",19);pstu->showInfo();delete pstu;shared_ptr<Stu> p(new Stu("liang",20));p->showInfo();shared_ptr<Stu> p1=p;//是拷贝p->showInfo();p1->showInfo();Shared_ptr<Stu> p2(new Stu("minmin",17));p2->showInfo();Shared_ptr<Stu> p3=p2;p3->showInfo();return 0;
}

交叉引用图:

交叉引用:

#include <iostream>
#include <memory>
using namespace std;class B;
class A{
public:shared_ptr<B> ptr_b;A(){cout << "A structure" << endl;}~A(){cout << "A destruct" << endl;}//a中的槽函数void slot_funtions(){cout << "加油!" << endl;}
};class B{
public:shared_ptr<A> ptr_a;B(){cout << "B structure" << endl;}~B(){cout << "B destruct" << endl;}void signal(){ptr_a->slot_funtions();}
};int main()
{shared_ptr<A> p_A(new A());shared_ptr<B> p_B(new B());p_A->ptr_b=p_B;//等号运算符重载p_B->ptr_a=p_A;p_B->signal();return 0;
}

存在 问题 :资源泄露,原因引用计数为2,return 时为1,不能析构

解决std::weak_ptr 的另一用法是打断 std::shared_ptr 所管理的对象组成的环状引用。若这种环被孤立(例如无指向环中的外部共享指针),则 shared_ptr 引用计数无法抵达零,而内存被泄露。能令环中的指针之一为弱指针以避免此情况。

weak_ptr破环指针

#include <iostream>
#include <memory>
using namespace std;class B;
class A{
public:weak_ptr<B> ptr_b;A(){cout << "A structure" << endl;}~A(){cout << "A destruct" << endl;}//a中的槽函数void slot_funtions(){cout << "加油!" << endl;}
};class B{
public:weak_ptr<A> ptr_a;B(){cout << "B structure" << endl;}~B(){cout << "B destruct" << endl;}void signal(){shared_ptr<A> temp=ptr_a.lock();if(nullptr!=temp)temp->slot_funtions();}
};int main()
{shared_ptr<A> p_A(new A());shared_ptr<B> p_B(new B());p_A->ptr_b=p_B;p_B->ptr_a=p_A;p_B->signal();return 0;
}

强弱智能指针的使用时机:

当智能指针对象直接托管堆上资源时,就是强引用智能指针。

当智能指针对象之间互相引用或赋值时,就使用弱引用智能指针,因为弱引用智能指针,不改变资源的引用记数,不操控堆上资源的属性与方法,他只是一个资源的观察者。这样可以避免堆上资源因交叉引用而出现的资源无法释放的问题。

所以weak_ptr也是shared_ptr的一个黄金搭档。作为强引用智能指针有力补充。

智能指针的问题与解决方案:删除器(Deletor)

#include <iostream>
#include <memory>
#include <functional>
using namespace std;
class A
{
public:A(){cout << "A的构造" << endl;}~A(){cout << "A的析构" << endl;}void showInfo(){cout << "hello" << endl;}
};
//使用函数对象作为自定删除器:
template <class T>
class Deletor
{
public:void operator()(T* p){delete []p;}
};
template <class T>
void deletor(T* p)//void (*)(T* p)
{delete []p;
}
int main()
{
//    1.使用默认删除器,来释放智能指针所托管的堆上连续多个空间的方式。
//    unique_ptr<A,default_delete<A[]>> ptr1(new A[5]);//    2.使用函数对象作为自定义删除器:
//    unique_ptr<A,Deletor<A>> ptr2(new A[5]);
//    ptr2->showInfo();//    3.使用函数指针作为自定义删除器:
//    unique_ptr<A,void (*)(A*)> ptr3(new A[5],deletor<A>);
//    ptr3->showInfo();//    4.使用包装器包装一个函数的调用对象,作为删除器对象。
//    unique_ptr<A,function<void (A*)>> ptr4(new A[5],deletor<A>);
//    ptr4->showInfo();//    5.使用包装器包装一个lambda表达式,作为自定义删除器。
//    unique_ptr<A,function<void (A*)>> ptr5(new A[5],[](A* p){ delete []p;});
//    ptr5->showInfo();//    shared_ptr<A> ptr(new A[5],default_delete<A[]>());
//    ptr->showInfo();
//    shared_ptr<A> ptr(new A[5],Deletor<A>());
//    ptr->showInfo();
//    shared_ptr<A> ptr(new A[5],deletor<A>);
//    ptr->showInfo();shared_ptr<A> ptr(new A[5],[](A* p){ delete []p;});ptr->showInfo();return 0;
}

【c++基础】第五章 RALL机制与智能指针相关推荐

  1. 《计算机应用基础》第05章在线测试,计算机应用基础.第五章测试及答案.doc

    计算机应用基础.第五章测试及答案 <计算机应用基础>第05章在线测试及答案 ?<计算机应用基础>第05章在线测试 剩余时间: 59:05 第一题.单项选择题(每题1分,5道题共 ...

  2. 计算机应用基础第五章试题及答案,计算机应用基础第五章测试题

    计算机应用基础第五章单元测试卷 考试时间:60分钟 总分:100分 一.单选题(每小题3分,共20题,计60分) 1.PowerPoint中,显示出当前被处理的演示文稿文件名的栏是( ) [A]工具栏 ...

  3. 郑大计算机应用基础试题5章,郑大计算机应用基础第五章答案

    郑大计算机应用基础第五章答案 (3页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 8.90 积分 1.如果要从第 2 张幻灯片跳转到第 8 张幻灯片,应使 ...

  4. 【C++ Primer 第5版 笔记】第12章 动态内存与智能指针

    转载:http://blog.csdn.net/wwh578867817/article/details/41866315 第 12 章 动态内存 与 智能指针 静态内存 用来保存:(1)局部stat ...

  5. 计算机基础第五章知识结构手写,福建省中等职业学校学业水平考试《计算机及其应用基础》考试大纲(试行)...

    I 考试性质 中等职业学校学生学业水平考试是根据国家及省中等职业学校教学标准及考试要求,由省教育厅组织实施的考试,主要衡量中等职业学校学生达到专业学习要求的程度,是保障中等职业学校教育教学质量的重要措 ...

  6. 从同步互斥机制到智能指针使用成本

    作者:CppExplore  http://www.cppblog.com/CppExplore/和http://blog.csdn.net/cppexplore同步发布 一 semaphore机制 ...

  7. Python基础 第五章

    目录 range()函数 创建range对象的三种方式 range类型的优点 循环结构 循环结构的流程图 循环的分类 语法结构 选择结构的if与循环结构while的区别 while循环 while循环 ...

  8. C# action 返回值_C#编程基础第五章:数组 - 张果

    一.什么是数组 数组用于存储若干相同类型的数据.在本章将介绍什么是数组以及数组的使用方法.了解数组之 后,将学习 foreach 循环以及数组的应用. 数组是一种数据结构,包含同一种类型的多个元素.也 ...

  9. web前端基础——第五章

    目录 五十.strong和b.em和i 五十一.引用标签 五十二.iframe嵌套页面 五十三.br与wbr 五十四.pre与code 五十五.map与area 五十六.embed与object 五十 ...

最新文章

  1. oracle中导入dmp数据注意事项
  2. bash: /opt/hisi-linux/x86-arm/arm-hisiv300-linux/target/bin/arm-hisiv300-linux-gcc: 没有那个文件或目录。...
  3. idea整合mybatis错误
  4. sprintf用法详解
  5. 永远不要放弃你所爱的人
  6. vscode开发前端常用插件
  7. ad采样信噪比与噪声_在AD转换中的过采样和噪声形成
  8. asp.net 中的 主从 新增,修改,删除。
  9. 用Windows Live Writer写51cto博客
  10. chromedriver 下载_解决ChromeDriver安装与配置问题
  11. Koa2 之文件上传下载
  12. mysql在故障切换时的事件调度器注意事项
  13. 显示硬件发展与视频开发系列(4)----移动时代
  14. 来客推电商快报之教育产业的未来发展
  15. PFPLD 人脸关键点检测
  16. 城市和经纬度一致性验证
  17. 200行Python实现的qq连连看辅助,用于学习,请不要拿去伤害玩家
  18. C语言printf()左对齐和右对齐
  19. 海思平台项目经验总结
  20. MY SQL 数据库升级

热门文章

  1. 有监督,无监督,半监督,弱监督、自监督学习
  2. python inplace
  3. FAILED: Execution Error, return code 1 from org.apache.hadoop.hive.ql.exec.mr.MapRedTask. ORC split
  4. html空格的使用 emsp ensp nbsp; thinsp; zwnj;  zwj;
  5. Postgresql 12.2 + PostGIS 3.0.1 安装部署手册
  6. Createprocess 函数运行出错的原因和解决办法
  7. 线性回归的损失函数与逻辑回归的损失函数
  8. dgiot百亿级物流标签轨迹时序数据压测
  9. Facebook与用户达成和解:为数据隐私问题赔偿5.5亿美元
  10. Learning Skeletal Articulations with Neural Blend Shapes(使用神经混合形状学习骨骼关节)SIGGRAPH 2021顶刊解析