前一段时间,有不少朋友问我关于智能指针的问题,并且反映经常会在面试中被面试官问到,所以今天小豆君就来讲讲我对智能指针的理解,希望能对大家有所帮助

既然讲智能指针,我们就先来看看它为什么会出现。

1 传统指针存在的问题

刚学C++的朋友,要数最令人头疼的问题莫过于指针了。

当你在堆上创建了一个对象时,系统就把这个对象的生命期完全交给了你,当用完之后,系统并不会回收资源,而是需要你来释放它。

那么,既然要负责对象的释放问题,就要知道什么时候释放和在哪里释放。如果你没有处理好这两个问题,就会造成内存泄漏或程序崩溃的问题。

//1 内存泄漏 str1所指的资源没有被释放
{string* str1 = new string("hello");string* str2 = new string("world");
}//2 多重释放,引起程序崩溃
{string* str1 = new string("hello");delete str1;//...delete str1;//引起程序崩溃
}

虽然上述例子很简单,指针的释放是在同一作用域中进行的,大家在编程中也能很容易避免上述代码中的问题。

但是,对于一个大型项目,在某一处创建的对象,可能并不会在对应作用域中释放,而是等到某些事件发生,异常处理等情况下才会去销毁对象,对于这样的问题往往是很难排查出来的。

所以,有必要引用一种机制来负责指针的自动销毁。而不是由程序员本身去手动销毁。智能指针恰恰就是这样的一种机制。

2 基于引用计数的智能指针原理分析

下面的这张图,解释了智能指针的原理。

1.当从堆上申请了一个资源时,我们就创建一个智能指针对象,使它指向这个资源,同时,在堆上申请一个用于计数的资源,让后来所有的指向该资源的对象都共享这个计数资源,这样,引用计数的个数就只有一份。

2.当将ptr1对象赋值给对象ptr2时,其共享的引用计数变为2。

3.删除ptr2对象时,其对应的引用计数减为1。

4.删除ptr1对象时,引用计数变为0,则释放资源。

3 智能指针的一种实现方式

在写代码前,先明确下代码的目的:

首先,智能指针是一个类,这样就可以使用构造函数和析构函数对引用计数进行维护;

其次,它要表现出指针的行为,并且使用起来也要像普通指针一样;

最后,智能指针对任何类型都可以使用,所以它应该是一个模板。

在阅读代码的时候,可以参考智能指针原理图。 代码并不困难,并且小豆君都做了注释

#pragma oncetemplate<class T>
class SharedPointer
{public://默认构造函数,内部指针,未指向任何资源,引用计数为0,因为它未与任何资源绑定SharedPointer() :m_refCount(nullptr), m_pointer(nullptr){}//构造函数,初始化时,指向一个已经分配好的资源SharedPointer(T* adoptTarget) :m_refCount(nullptr), m_pointer(adoptTarget){addReference();}//构造函数,使用其它对象创建新对象SharedPointer(const SharedPointer<T>& copy):m_refCount(copy.m_refCount), m_pointer(copy.m_pointer){addReference();}//析构函数,引用计数递减,当为0时,释放资源virtual ~SharedPointer(){removeReference();}//赋值操作//当左值被赋值时,表明它不再指向所指的资源,故引用计数减一//之后,它指向了新的资源,所以对应这个资源的引用计数加一SharedPointer<T>& operator=(const SharedPointer<T>& that){if (this != &that){removeReference();this->m_pointer = that.m_pointer;this->m_refCount = that.m_refCount;addReference();}return *this;}//判断是否指向同一个资源bool operator==(const SharedPointer<T>& other){return m_pointer == other.m_pointer;}bool operator!=(const SharedPointer<T>& other){return !operator==(other);}//指针解引用T& operator*() const{return *m_pointer;}//调用所知对象的公共成员T* operator->() const{return m_pointer;}//获取引用计数个数int GetReferenceCount() const{if (m_refCount){return *m_refCount;} else{return -1;}}protected://当为nullpter时,创建引用计数资源,并初始化为1//否则,引用计数加1。void addReference(){if (m_refCount){(*m_refCount)++;}else{m_refCount = new int(0);*m_refCount = 1;}}//引用计数减一,当变为0时,释放所有资源void removeReference(){if (m_refCount){(*m_refCount)--;if (*m_refCount == 0){delete m_refCount;delete m_pointer;m_refCount = 0;m_pointer = 0;}}}private:int * m_refCount;T   * m_pointer;
};

4 测试

以下程序是对上述代码的测试,大家可以看看实际的运行效果

#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include "SharedPointer.h"using namespace std;
class MyClass
{public:~MyClass(){cout << "释放MyClass(" << _id << ")n";}MyClass(int i) :_id(i){}void Print() const{cout << "MyClass(" << _id << ")" << endl;}
private:int _id;
};int main()
{{MyClass* px = new MyClass(1);SharedPointer<MyClass> ap(px);SharedPointer<MyClass> bp = ap;SharedPointer<MyClass> cp;cout << "ap的引用计数(2): "<< ap.GetReferenceCount() << endl;cout << "将ap赋值给cpn";cp = ap;cout << "ap的引用计数(3): "<< ap.GetReferenceCount() << endl;MyClass* qx = new MyClass(5);SharedPointer<MyClass> dp(qx);ap = dp;cout << "ap的引用计数(2): "<< ap.GetReferenceCount() << endl;cout << "dp的引用计数(2): "<< dp.GetReferenceCount() << endl;//"像指针一样使用智能指针"dp->Print();(*cp).Print();}cin.get();
}

运行结果:

我们跟踪测试程序,通过运行结果,查看引用计数的变化,并且,当计数为0时,自动释放资源。

好了,关于智能指针的原理示例程序就给大家介绍到这里了,以后再有面试官问你,就可以明明白白的给他解释了。其实,标准库中也有对应库实现shared_ptr,而且它比小豆君的要复杂多了。

智能指针虽然在一定程序上解决了裸指针存在的问题,但是还是有很多不足的地方,例如本例还缺少资源共享时的保护措施,环形引用等问题,等有机会小豆君再跟大家分享吧。

欢迎关注微信公众号-小豆君Qt分享

qt 如何 指针 自动 释放内存_要是面试官再问你智能指针的问题,就拿这篇文章“盘他”!!!...相关推荐

  1. websphere mq 查看队列中是否有数据_如果面试官再问你消息队列,就把这篇甩给他!...

    ★★★建议星标我们★★★ 公众号改版后文章乱序推荐,希望你可以点击上方"Java进阶架构师",点击右上角,将我们设为★"星标"!这样才不会错过每日进阶架构文章呀 ...

  2. java执行sql文件_面试官:MyBatis SQL是如何执行的?把这篇文章甩给他

    初识 MyBatis MyBatis 是第一个支持自定义 SQL.存储过程和高级映射的类持久框架.MyBatis 消除了大部分 JDBC 的样板代码.手动设置参数以及检索结果.MyBatis 能够支持 ...

  3. hashmap存多少条数据_干货 | 面试官想问的HashMap,都在这一篇里面了!

    来源公众号:非科班的科班 本文思维导图 HashMap简介 HashMap 是很常用的一种集合框架,其底层实现方式在 JDK 1.7和 JDK 1.8中却有很大区别.HashMap 是用来存储数据的, ...

  4. 面试问到java并发_那些面试官必问的JAVA多线程和并发面试题及回答

    Java多线程面试问题 1. 进程和线程之间有什么不同? 一个进程是一个独立(self contained)的运行环境,它可以被看作一个程序或者一个应用.而线程是在进程中执行的一个任务.Java运行环 ...

  5. java限制只能修改一次订单_面试官再问你怎么修改订单,就把这篇甩给他

    从下单开始.支付.发货,收货,每一个环节,都少不了更新订单,每一次更新又需要同时更新好几张表. 这些操作可能被随机分布到很多台服务器上执行,服务器有可能故障,网络有可能出问题. 那么如何才能保证订单服 ...

  6. nginx是干嘛用的_上帝视角——面试官常问的nginx的几个问题

    无聊科普下关于nginx会问哪些问题.其实没啥好问的.Nginx基本就会问下面这些问题 问题1:Nginx是用来干嘛的? Nginx是一个高性能的HTTP和反向代理服务器,这个基本是用来前端服务器集群 ...

  7. 面试项目亮点_大厂面试官常问的亮点我们项目中怎么做出?(软技能)

    这篇作为之前一篇文章的延续,以及对过去3个月我模拟面试过的30多位同学的面试情况总结,我们来聊一下怎么做出亮点 你负责的业务是什么?(学会发现问题) 之前在群里参加活动的同学,有不少说在小公司,被业务 ...

  8. RAMMAP(运行内存清理工具)自动释放内存,并利用pyqt5制作图形界面

    上一篇文章用python制作了一个自动清理内存的程序,利用cmd端口调用Rammap,不过只是做了一个托盘图标,这回用pyqt5做一个简单的图形界面,并实现对自动清理模式的一些设置 首先利用pyqt5 ...

  9. 【安卓培训 App培训】自动释放内存!Android图片库Fresco

    [安卓培训 零基础到就业]QQ:3163173005 摘要:Fresco是Facebook最新推出的一款用于Android应用中展示图片的强大图片库,可以从网络.本地存储和本地资源中加载图片.其中的D ...

最新文章

  1. 9.Matlab中的repmat,clock,rand以及seed,state,twister参数
  2. 使用结构体数组统计男、女人数,计算全体学生的平均年龄、平均成绩,并将高于平均成绩的学生信息输出
  3. PHP通过DOM操作XML
  4. 财务用计算机可以一次摊销,购入的电脑可以一次性计提折旧吗
  5. 码农们不得不重视的问题
  6. Linux 列出文件列表命令ls
  7. srs代码学习(1)--listen建立过程
  8. [折半搜索][has] Jzoj P4250 路径
  9. python 生成pdf_如何使用Python生成PDF?
  10. VUE.JS 组件化开发实践
  11. UI素材干货模板|网页“按钮”组件,教你要如何设计!
  12. 枚举一个集合的所有子集
  13. linux大鹏命令百篇
  14. InnoDB的RR隔离级别能否防止“幻读”
  15. e: 无法定位软件包 python-pip_关于Sony镜头不同系列定位的最全面分析
  16. 林子雨 慕课答案2021新版
  17. Python】Scrapy抓取多玩Gif图片
  18. 图扑软件2D与2.5D案例合集|智慧园区、数据中心、SMT 生产线...
  19. 试用样机BL2480T测评
  20. 什么是JAVAweb?

热门文章

  1. Android字符串资源及其格式化
  2. Java对象创建、分配、布局、访问小析(HotSpot虚拟机)(二)
  3. cocos JS 定时器
  4. C# 3.0新语言特性和改进(一)
  5. Android okHttp网络请求之缓存控制Cache-Control
  6. Java方法区和运行时常量池溢出问题分析(转)
  7. linux mint关于web开发的相关环境配置
  8. Java中文件路径的写法
  9. 数据库基本SQL语句大全
  10. 雇佣黑客组织利用 3Ds Max 恶意软件攻击全球企业