C++11——智能指针

本文将解决一下几个问题

1.什么是智能指针
2.为什么需要之智能指针
3.智能指针的使用场景

智能指针

RAII:是一种利用对象声明周期来控制的程序资源(如内存、文件句柄、网络连接、互斥量等)的技术

在对象构造的时候获取资源,接着控制资源的访问使之在对象的声明周期内始终有效,最后在对象析构的时候释放资源。这样做有2个好处

  • 不需要显示的释放资源
  • 对象所需要的资源在其声明周期有效,出声明周期会自动释放

为什么需要智能指针

C++中申请内存一般都是用new来申请,用delete 来释放对应的内存(资源)。内存的正确申请和释放其实很容易出现问题的,比如有时忘记了释放,或者申请了new[],但是直接delete,而不是delete [],如果是内置类型,如果有的话(什么都不做),不会出现报错,但是如果是内置类型就会出现内存泄露或者其他未定义的行为。 而这时智能指针出现了。

所谓智能指针其实就是类模版,它可以创建任意类型的指针(在创建的时候调用构造函数),在对象的声明周期结束的之前,会去调用自身的析构函数释所指向的空间。

template<class T>
class SmartPtr
{
public:SmartPtr(T* ptr):_ptr(ptr){}~SmartPtr(){if(ptr != nullptr){delete ptr;ptr = nullptr;}}T& operator*(){return *ptr;}T* operator->(){return ptr;}
private:T* 	_ptr;
};

在这里插入图片描述

输出结果:
0x7ffd92fe90d0
0x7ffd92fe90e0

C++98版本的库中提供了auto_ptr的智能指针。

auto_ptr

auto的实现原理:管理权转移的思想,下面是一个简单版本的auto_ptr

template<class T>class auto_ptr{public:auto_ptr(T* ptr):_ptr(ptr){}auto_ptr(auto_ptr<T>& sp):_ptr(sp._ptr){// 管理权转移sp._ptr = nullptr;}auto_ptr<T>& operator=(auto_ptr<T>& ap){// 检测是否为自己给自己赋值if (this != &ap){// 释放当前对象中资源if (_ptr)delete _ptr;// 转移ap中资源到当前对象中_ptr = ap._ptr;ap._ptr = NULL;}return *this;}~auto_ptr(){if (_ptr){cout << "delete:" << _ptr << endl;delete _ptr;
}}// 像指针一样使用T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;};

在这里插入图片描述

这里有一个很严重的问题,就是sp4拷贝构造sp3的时候,就会导致sp3指针悬空。

unique_ptr

所以后来C++11开始提供了unique_ptr
unique_ptr实现原理:就是防止拷贝,下面是一份模拟简化的unique_ptr

template<class T>class unique_ptr{public:unique_ptr(T* ptr):_ptr(ptr){}~unique_ptr()
{if (_ptr){cout << "delete:" << _ptr << endl;delete _ptr;}}// 像指针一样使用T& operator*(){return *_ptr;}T* operator->(){return _ptr;}unique_ptr(const unique_ptr<T>& sp) = delete;unique_ptr<T>& operator=(const unique_ptr<T>& sp) = delete;private:T* _ptr;};

这时如果再继续上面的代码,就会直接报错,不会导致用户如果不知道内部实现可能会踩坑的情况。

shared_ptr

后来C++11中开始提供了更靠谱的并且支持拷贝的shared_ptr
shared_ptr实现原理:通过引用计数来实现多个shared_ptr对象之间共享资源。

1.其中shared_ptr在其内部,给每一个资源都维护了一份计数,用来记录该资源被多少个对象共享
2.在对象被销毁的时候,如果自己不再使用该资源了,引用计数会减少一。
3.如果引用计数是0,那么就说明是最后一个使用该资源的对象,必须释放资源。
4.如果不是0,就说明还有其他对象使用该资源,不用释放,否则其他对象就是野指针了。

下面是一份简化的模拟实现shared_ptr

template<class T>class shared_ptr{public:shared_ptr(T* ptr):_ptr(ptr), _pcount(new int(1)), _pmtx(new mutex){}~shared_ptr(){Release();}void Release(){_pmtx->lock();if (--(*_pcount) == 0){cout << "delete:" << _ptr << endl;delete _ptr;delete _pcount;// delete _pmtx;  如何解决?}_pmtx->unlock();}void AddCount(){_pmtx->lock();++(*_pcount);_pmtx->unlock();}shared_ptr(const shared_ptr<T>& sp):_ptr(sp._ptr), _pcount(sp._pcount), _pmtx(sp._pmtx){AddCount();}// sp1 = sp4// sp1 = sp1;// sp1 = sp2;shared_ptr<T>& operator=(const shared_ptr<T>& sp){if (_ptr != sp._ptr){Release();_ptr = sp._ptr;_pcount = sp._pcount;_pmtx = sp._pmtx;AddCount();}return *this;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* get(){return _ptr;}int use_count(){return *_pcount;}private:T* _ptr;int* _pcount;mutex* _pmtx;};

shared_ptr线程安全的问题

1.要注意的是shared_ptr对象中引用计数线程安全的,因为引用计数就是用锁来实现的。
2.shared_ptr本质还是在堆上申请释放的,如果两个线程去访问,就有可能出现线程安全的问题。

shared_ptr循环引用

shared_ptr中,循环引用是一个很大的问题。下面的代码中,node1有node2对象的资源,这样就会导致循环引用,因为node1和node2引用计数都是1,它们都不会自动释放掉资源,那么超出了声明周期,也不会调用析构函数去释放掉相对应的资源,从而导致了内存泄露。

struct ListNode
{int _data;shared_ptr<ListNode> _prev;shared_ptr<ListNode> _next;~ListNode(){cout <<"~ListNode" <<endl;}
};
int main()
{shared_ptr<ListNode> node1(new ListNode);shared_ptr<ListNode> node2(new ListNode);cout << node1.use_count() << endl;cout << node2.use_count() << endl;node1->_next = node2;node2->_prev = node1;cout << node1.use_count() << endl;cout << node2.use_count() << endl;return 0;
}

在这里插入图片描述

weak_ptr

weak_ptr:弱引用智能指针,用于解决shared_ptr的循环引用的问题。
weak_ptr可以观察shared_ptr的声明周期,但不会增加引用计数。
可以通过shared_ptr的成员函数weak_ptr::lock来获取一个指向同一个对象的shared_ptr.

std::weak_ptr<int> weakPtr = ptr1;
if(std::shared_ptr<int> sharedPtr = weakPtr.lock())
{//使用shared_ptr
}

如果不是new出来的对象如何通过智能指针来管理呢?

C++中,智能指针(shared_ptr,unique_ptr)主要是用于管理动态申请的内存,即使用new关键字来管理内存,如果想用智能指针来管理不是new出来的对象,那么就可以尝试自定义删除器

template<class T>  
struct FreeFunc {  void operator()(T* ptr) {  cout << "free:" << ptr << endl;  free(ptr);  }  
};  template<class T>  
struct DeleteArrayFunc {  void operator()(T* ptr) {   cout << "delete[]:" << ptr << endl;  delete[] ptr;   }  
};  
int main() {  FreeFunc<int> freeFunc;  shared_ptr<int> sp1((int*)malloc(sizeof(int)), freeFunc);  DeleteArrayFunc<int> deleteArrayFunc;  shared_ptr<int[]> sp2((int*)malloc(10 * sizeof(int)), deleteArrayFunc); // 注意这里应该是int[],并且分配了10个整数的空间

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/273385.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Navicat、phpMyAdmin本地安装

一、Navicat安装 注&#xff1a;本次关于Navicat的安装版本为Navicat160的windows版本 1、从官方下载安装包并安装 官方下载链接为 ​​https://download.navicat.com.cn/download/navicat160_premium_cs_x64.exe​​ 下载完成后&#xff0c;直接进行安装即可&#xff08;…

Unity 整体界面淡入淡出效果

在Unity中&#xff0c;如果我们要实现控制多个组件同时淡出&#xff0c;同时淡入的效果&#xff0c;可以使用DOTween插件实现。 如图&#xff0c;一个页面中带有背景&#xff0c;一张图片&#xff0c;一个文本&#xff0c;一个滑动条。 要实现以上界面的整体淡入淡出&#xff…

C语言连接【MySQL】

稍等更新图片。。。。 文章目录 安装 MySQL 库连接 MySQLMYSQL 类创建 MySQL 对象连接数据库关闭数据库连接示例 发送命令设置编码格式插入、删除或修改记录查询记录示例 参考资料 安装 MySQL 库 在 CentOS7 下&#xff0c;使用命令安装 MySQL&#xff1a; yum install mysq…

R语言:多值提取到点

ArcGIS中有相关工具实现多值提取到点的功能&#xff0c;在这里&#xff0c;我将使用R语言进行操作&#xff1a; library(dplyr) library(readxl) library(sf) library(raster)setwd("D:/Datasets") Bio <- stack(paste0("D:/Datasets/Data/worldclim2_1km/…

【C++】了解一下STL

个人主页 &#xff1a; zxctscl 如有转载请先通知 STL 1. 什么是STL2. STL的版本3. STL的六大组件4. STL的重要性5. 如何学习STL6. STL的缺陷 1. 什么是STL STL(standard template libaray-标准模板库)&#xff1a;是C标准库的重要组成部分&#xff0c;不仅是一个可复用的组件…

SkyEye:助力飞行器状态控制系统仿真

飞行器与常见的航天器一样&#xff0c;属于安全关键领域的大型复杂设备&#xff0c;对安全性、可靠性有着极高的要求。为保证稳定飞行&#xff0c;需要对目标对象进行实时跟踪&#xff0c;通过发出正确的修正偏差指令来操纵飞行器改变飞行姿态&#xff0c;因此对飞行器状态控制…

SSM框架,MyBatis-Plus的学习(下)

条件构造器 使用MyBatis-Plus的条件构造器&#xff0c;可以构建灵活高效的查询条件&#xff0c;可以通过链式调用来组合多个条件。 条件构造器的继承结构 Wrapper &#xff1a; 条件构造抽象类&#xff0c;最顶端父类 AbstractWrapper &#xff1a; 用于查询条件封装&#xf…

堆宝塔(Python)

作者 陈越 单位 浙江大学 堆宝塔游戏是让小朋友根据抓到的彩虹圈的直径大小&#xff0c;按照从大到小的顺序堆起宝塔。但彩虹圈不一定是按照直径的大小顺序抓到的。聪明宝宝采取的策略如下&#xff1a; 首先准备两根柱子&#xff0c;一根 A 柱串宝塔&#xff0c;一根 B 柱用于…

python INI文件操作与configparser内置库

目录 INI文件 configparser内置库 类与方法 操作实例 导入INI文件 查询所有节的列表 判断某个节是否存在 查询某个节的所有键的列表 判断节下是否存在某个键 增加节点 删除节点 增加节点的键 修改键值 保存修改结果 获取键值 获取节点所有键值 其他读取方式 …

Python SSH协议库之paramiko使用详解

概要 在网络编程中,远程操作是一项非常常见的需求,特别是在服务器管理和自动化任务执行方面。Python提供了许多库来实现远程操作,其中Paramiko是一个备受欢迎的选择。Paramiko是一个纯Python编写的SSH协议库,它提供了一种简单而强大的方式来执行远程命令、上传和下载文件等…

spring-data-elasticsearch官方文档解读(部分)

Spring Data Elasticsearch 这里主要学习的是4.4.16版本的文档 1. 版本 下表显示了 Spring Data 发行版系列使用的 Elasticsearch 版本和其中包含的 Spring Data Elasticsearch 版本&#xff0c;以及引用该特定 Spring Data 发行版系列的 Spring Boot 版本。给出的 Elastics…

【Spring Boot 3】获取已注入的Bean

【Spring Boot 3】获取已注入的Bean 背景介绍开发环境开发步骤及源码工程目录结构总结 背景 软件开发是一门实践性科学&#xff0c;对大多数人来说&#xff0c;学习一种新技术不是一开始就去深究其原理&#xff0c;而是先从做出一个可工作的DEMO入手。但在我个人学习和工作经历…

深入理解Servlet

目录&#xff1a; ServletWeb开发历史Servlet简介Servlet技术特点Servlet在应用程序中的位置Tomcat运行过程Servlet继承结构Servlet生命周期Servlet处理请求的原理Servlet的作用HttpServletRequest对象HttpServletResponse对象ServletContext对象ServletConfig对象Cookie对象与…

面向对象的编程语言是什么意思?——跟老吕学Python编程

面向对象的编程语言是什么意思&#xff1f;——跟老吕学Python编程 面向对象是什么意思&#xff1f;面向对象的定义面向对象的早期发展面向对象的背景1.审视问题域的视角2.抽象级别3.封装体4.可重用性 面向对象的特征面向对象的开发方法面向对象程序设计基本思想实现 面向对象的…

RocketMQ存储设计深度解析

引言 在分布式系统中&#xff0c;消息中间件扮演着至关重要的角色&#xff0c;它负责系统间异步消息的传递&#xff0c;确保信息可靠传输。Apache RocketMQ&#xff08;以下简称RocketMQ&#xff09;是这一领域中的一个优秀代表。RocketMQ以其高性能、高可靠性和高扩展性赢得了…

未来城市:探索数字孪生在智慧城市中的实际应用与价值

目录 一、引言 二、数字孪生与智慧城市的融合 三、数字孪生在智慧城市中的实际应用 1、智慧交通管理 2、智慧能源管理 3、智慧建筑管理 4、智慧城市管理 四、数字孪生在智慧城市中的价值 五、挑战与展望 六、结论 一、引言 随着科技的飞速发展&#xff0c;智慧城市已…

AI论文速读 | 【综述】城市计算中跨域数据融合的深度学习:分类、进展和展望

题目&#xff1a;Deep Learning for Cross-Domain Data Fusion in Urban Computing: Taxonomy, Advances, and Outlook 作者&#xff1a;Xingchen Zou, Yibo Yan, Xixuan Hao, Yuehong Hu, Haomin Wen&#xff08;温皓珉&#xff09;, Erdong Liu, Junbo Zhang&#xff08;张钧…

GitHub Desktop的常用操作【图形化】

文章目录 【1】仓库的创建和删除【2】文件操作【3】分支原理与分支操作1.分支创建2.分支合并 【4】标签 【1】仓库的创建和删除 在本地创建一个新的仓库&#xff1a; 然后输入仓库的名称&#xff0c;描述&#xff0c;并选择路径&#xff1a; 点击完后就发现我们的仓库创建好…

线性代数(一)——向量基础

向量基础 1、向量和线性组合2、向量的模和点乘3、矩阵4、参考 线性代数的核心是向量的加和乘两种运算的组合&#xff0c;本篇博客为线性代数的一个引子&#xff0c;主要从向量、线性组合和矩阵逐步引出线性代数的相关知识。 1、向量和线性组合 首先介绍的是向量相关&#xff0…

(每日持续更新)jdk api之SequenceInputStream基础、应用、实战

博主18年的互联网软件开发经验&#xff0c;从一名程序员小白逐步成为了一名架构师&#xff0c;我想通过平台将经验分享给大家&#xff0c;因此博主每天会在各个大牛网站点赞量超高的博客等寻找该技术栈的资料结合自己的经验&#xff0c;晚上进行用心精简、整理、总结、定稿&…