【C++11】智能指针的定义 和 种类 及 使用

智能指针

定义

为什么需要智能指针

在C++中,动态分配内存是一项常见的任务,但手动管理分配和释放内存可能会导致很多问题,如内存泄漏、悬垂指针以及多次释放同一块内存等。为了避免这些问题,引入了智能指针的概念,它们提供了自动化的内存管理。

以下是智能指针可以解决的一些问题
在这里插入图片描述


智能指针的使用 及 原理

智能指针的原理

RAII

RAII

RAII(Resource Acquisition Is Initialization)是一种编程技术和设计原则,它通过将资源的获取与对象的初始化绑定在一起来管理资源。在使用 RAII 时,资源的获取和释放操作被封装在对象的构造函数和析构函数中,利用了对象的生命周期管理资源的自动分配和释放。

该技术的 基本思想

  • 对象的构造函数负责获取资源(如内存、文件句柄、数据库连接等)
  • 析构函数负责释放这些资源。
  • 通过使用 RAII,可以确保在对象离开作用域时,无论是正常退出还是异常退出,资源都会被正确释放,从而避免了资源泄漏的问题

下面利用 RAII 思想实现 一个SmartPtr 的代码:

// 利用RAII思想实现的SmartPtr类
template<class T>
class SmartPtr
{
public:// 构造函数 - 获取资源SmartPtr(T* ptr = nullptr):_ptr(ptr){}// 析构函数 - 释放资源~SmartPtr(){if (_ptr){cout << "Delete:" << _ptr << endl;}}// 实现自定义指针类需要的函数// 重载 operator* 函数用于实现指针解引用操作,允许通过对象的指针访问该指针所指向的对象。返回类型为 T&,表示对指向 T 类型对象的引用。// 重载 operator-> 函数用于实现指针的箭头操作,允许通过对象的指针直接调用该指针所指向对象的成员函数或成员变量。返回类型为 T* ,表示指向 T 类型对象的指针。T& operator*(){return *_ptr;}T* operator->(){return _ptr;}
private:T* _ptr;
};

上述代码实现了简要的智能指针的功能,智能指针的原理即

  1. RAII特性
  2. 重载operator*opertaor-> 具有和指针一样的行为

智能指针的种类


auto_ptr

auto_ptrC++98标准中提供的智能指针,它具有独占性质,意味着同一时间只能有一个auto_ptr拥有对特定对象的所有权。

当一个auto_ptr被赋值给另一个auto_ptr时,所有权会被转移,原来的auto_ptr将不再拥有该对象的所有权 (所有权转移) 。这种特性可以用于简单的资源管理,但也容易导致潜在的问题。

由于 auto_ptr 的所有权转移特性,在某些情况下可能会导致意外的行为
例如:

  • 如果将auto_ptr存储在标准容器中,容器的复制或赋值操作会导致对象所有权的转移,从而使得容器内的指针失效。

此外:

  • auto_ptr在异常处理机制方面也存在问题,如果在析构过程中抛出异常,可能会导致资源泄漏。

代码分析

下面对 auto_ptr 的模拟实现,展示了 auto_ptr 的性质

namespace aiyimu
{// C++98: auto_ptr 有一定缺陷template<class T>class auto_ptr{public:auto_ptr(T* ptr=nullptr) // 构造:_ptr(ptr){}~auto_ptr()	// 析构{if (_ptr){cout << "Delete:" << _ptr << endl;delete _ptr;}}// 指针特性T& operator*(){return *_ptr;}T* operator->(){return _ptr;}// 拷贝构造auto_ptr(auto_ptr<T>& ap):_ptr(ap._ptr){// 所有权 / 管理权 转移ap._ptr = nullptr;}// 赋值重载auto_ptr operator=(auto_ptr<T>& ap){// 给自己赋值不执行操作if (this != &ap){if (_ptr) //如果_ptr指向了对象,则删除其指向{cout << "Delete:" << _ptr <<endl;delete _ptr;}_ptr = ap._ptr;ap._ptr = nullptr;}return *this;}private:T* _ptr;};
}

通过下面对 std::auto_ptr 的调用可以看出其缺点

void Test_auto_ptr()
{std::auto_ptr<int> sp1(new int);std::auto_ptr<int> sp2(sp1); //此时所有权转移// 此时的 sp1 悬空*sp2 = 20;cout << *sp2 << endl;cout << *sp1 << endl;
}

该函数在调用执行后会报错,原因如下:

  1. sp2通过拷贝构造函数从sp1获取了所有权。这导致原来的sp1变为悬空指针,指向的内存区域不再有效。
  2. 在对悬空指针sp1进行解引用操作时,会导致未定义行为。因此,打印*sp1的语句会产生不可预测的结果。
  3. 由于std::auto_ptr的问题,该代码没有正确处理资源所有权的转移和管理。

综上所述,建议使用C++11标准中提供的智能指针类型,如std::unique_ptr、std::shared_ptr或std::weak_ptr,以避免这些问题,并更好地管理资源所有权和避免悬空指针的情况。


unique_ptr

unique_ptr 的性质:

  1. 拥有性unique_ptr 是一个独占所有权的智能指针,它禁止两个 unique_ptr 对象指向同一个对象
  2. 所有权转移:与 auto_ptr 一样,unique_ptr 支持所有权的转移。通过移动语义,可以将一个 unique_ptr 的所有权从一个对象转移到另一个对象,从而避免了资源的复制和多次删除。
  3. 自动释放:当 unique_ptr 被销毁或者重新赋值时,它会自动删除所拥有的资源避免了内存泄漏
  4. 零开销unique_ptr 本身非常轻量,不引入额外的开销,且常被优化为和裸指针一样的大小和性能。

代码分析原理

通过下面的模拟实现 理解其 原理:

namespace
{// unique_ptr 是一个独占所有权的智能指针,它禁止两个 unique_ptr 对象指向同一个对象。// 当尝试使用拷贝构造函数或赋值重载运算符来创建或赋值 unique_ptr 对象时,编译器会报错。template<class T>class unique_ptr{public:// 使用 delete 关键字 禁用其拷贝和赋值unique_ptr(unique_ptr<T>& ap) = delete;unique_ptr<T>& operator=(unique_ptr<T>& ap) = delete;// 构造unique_ptr(T* ptr = nullptr): _ptr(ptr){}// 析构~unique_ptr(){if (_ptr){cout << "Delete" << _ptr << endl;delete _ptr;_ptr = nullptr;}}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private: T* _ptr;};
};

下面是一段 使用代码:

class A
{
public:~A(){cout << "~A()" << endl;}//private:int _a1 = 0;int _a2 = 0;
};void test_unique_ptr()
{aiyimu::unique_ptr<A> up1(new A);// 这样的拷贝行为违反了 unique_ptr 的意图,unique_ptr 应该是独占资源的智能指针// aiyimu::unique_ptr<A> up2(up1);up1->_a1++;up1->_a2++;cout << "up1->_a1: " << up1->_a1 << endl;cout << "up1->_a2: " << up1->_a2 << endl;// 输出结果: 1 1
}

总结:unique_ptr 是一种独占所有权的智能指针,不允许直接进行拷贝行为。如果需要共享资源,可以使用 shared_ptr 来实现。


shared_ptr

性质

  1. 共享性:shared_ptr 可以与其他 shared_ptr 共享所管理的资源。通过内部的引用计数机制,shared_ptr在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共享。
  2. 拷贝语义:shared_ptr 支持拷贝构造函数和拷贝赋值运算符。当一个 shared_ptr 被拷贝给另一个 shared_ptr 时,它们将共享同一个资源,引用计数会增加
  3. 自动释放:当最后一个引用计数为零时,即没有任何 shared_ptr 实例指向某个资源时,shared_ptr 自动释放资源。这可以避免了资源泄漏。
  4. 循环引用处理:shared_ptr 使用弱引用计数(weak_ptr)来解决循环引用问题。循环引用是指两个或多个对象相互持有对方的 shared_ptr 实例,导致引用计数无法归零。通过将其中一个 shared_ptr 转换为 weak_ptr,可以打破循环引用,使资源正确释放。
  5. 定制删除器:与 unique_ptr 类似,shared_ptr 也 支持定制的删除器 ,以实现对不同类型资源的特殊释放操作。

其中的重点在于引用计数:

  • 对象被销毁时(析构函数调用),资源不再使用,对象的引用计数减一
  • 如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源
  • 如果引用计数不是0,就说明仍有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了

代码分析

namespace aiyimu
{// shared_ptr 是 C++ 中一种共享所有权的智能指针。// 与 unique_ptr 只能由一个对象拥有所有权不同,shared_ptr 允许多个 shared_ptr 对象同时管理同一个对象template<class T>class shared_ptr{public:// 构造shared_ptr(T* ptr = nullptr):_ptr(ptr),_pCount(new int(1)){}// 析构~shared_ptr(){Release();}// 拷贝构造shared_ptr(const shared_ptr<T>& sp):_ptr(sp._ptr),_pCount(sp._pCount){++(*_pCount); // 计数++}// 赋值重载shared_ptr<T>& operator=(const shared_ptr<T>& sp){// 自己赋值自己,不做操作if (_ptr == sp._ptr){return *this;}Release();// 共享新资源,计数++_ptr = sp._ptr;_pCount = sp._pCount;(*_pCount)++;//返回*thisreturn *this;}T& operator*(){assert(_ptr != nullptr); // 断言指针非空return *_ptr;}T* operator->(){assert(_ptr != nullptr); // 断言指针非空return _ptr;}// 返回计数个数int use_count(){return *_pCount;}// 获取指针T* get() const{return _ptr;}// Release 函数,用于释放资源并销毁 shared_ptr 对象void Release() {if (--(*_pCount) == 0) {cout << "Delete:" << _ptr << endl;delete _ptr;delete _pCount;}}private:T* _ptr;int* _pCount; // 引用计数};
};

下面的代码将展示 shared_ptr 的使用 和 性质验证:

void test_shared_ptr1()
{aiyimu::shared_ptr<A> sp1(new A);aiyimu::shared_ptr<A> sp2(sp1);aiyimu::shared_ptr<A> sp3(sp1);sp1->_a1++;sp1->_a2++;cout << "sp2->_a1 : _a2 ->" << sp2->_a1 << ":" << sp2->_a2 << endl; // 1 1// shared_ptr 使sp1 sp2 共享一块内存,即两者的_a1,_a2的值是同步的sp2->_a1++;sp2->_a2++;cout << "sp1->_a1 : _a2 ->" << sp1->_a1 << ":" << sp1->_a2 << endl; // 2 2
}

下面将介绍 与 shared_ptr 配合使用的 weak_ptr


weak_ptr

weak_ptr 是 C++ 标准库(C++11 及以后版本)中与 shared_ptr 配合使用的智能指针类, 用于解决 shared_ptr 的循环引用问题

特性

  • 弱引用weak_ptr 是一种弱引用,它可以观测(但不拥有)一个由 shared_ptr 管理的对象。通过 shared_ptr 创建 weak_ptr,可以同时存在多个 weak_ptr 实例观测同一个资源。
  • 不会增加引用计数weak_ptr 不会增加所管理资源的引用计数。即使存在 weak_ptr 对象观测某个资源,资源的引用计数也不会增加,因此不会影响资源的生命周期。
  • 检查资源是否有效:可以使用 expired() 函数检查 weak_ptr 所观测的资源是否还存在。如果资源已经被释放(即引用计数为零),expired() 返回 true,否则返回 false。
  • 获取共享指针:可以使用 lock() 函数将 weak_ptr 转换为 shared_ptr,得到与之关联的共享指针。如果资源仍然存在,则返回一个有效的 shared_ptr;如果资源已被释放,则返回一个空的 shared_ptr。

代码分析

下面是一个简化版本的 weak_ptr 的模拟实现:

// weak_ptr 是 C++ 中一种弱引用智能指针,用于解决 shared_ptr 的循环引用问题。template<class T>class weak_ptr{public:weak_ptr():_ptr(nullptr){}// 该构造函数接受一个shared_ptr<T>类型的参数sp,将其内部指针通过get()函数获取后赋值给_ptr。// 可以创建一个weak_ptr对象来观测所传入的shared_ptr所管理的资源。weak_ptr(const shared_ptr<T>& sp):_ptr(sp.get()){}// 该构造函数接受一个weak_ptr<T>类型的参数wp,将其内部指针直接赋值给_ptr。// 可以创建一个新的weak_ptr对象,其观测的资源与原wp对象相同。weak_ptr(const weak_ptr<T>& wp):_ptr(wp._ptr){}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}// 赋值weak_ptr<T>& operator=(const shared_ptr<T>& sp){_ptr = sp.get();return *this;}private:T* _ptr;};

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

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

相关文章

LiveGBS流媒体平台GB/T28181功能-海康NVR摄像机自带物联网卡摄像头注册GB/T28181国标平台看不到设备的时候如何抓包及排查

海康大华宇视华为等硬件NVR摄像机注册到LiveGBS国标平台看不到设备的时候如何抓包及排查 1、设备注册后查看不到1.1、是否是自带物联网卡的摄像头1.2、关闭萤石云1.3、防火墙排查1.4、端口排查1.5、IP地址排查1.6、设备TCP/IP配置排查1.7、设备多网卡排查1.8、设备接入配置参数…

Docker(四)

文章目录 1. docker其他命令补充2. docker-registry使用3. docker-hub的使用4. 企业级私有仓库harbor4.1 harbor安装4.2 harbor配置https4.3 harbor常见使用4.3.1 harbor新建项目仓库4.3.2 harbor创建用户4.3.3 harbor仓库管理4.3.4 harbor复制管理4.3.5 harbor删除镜像 5. doc…

K8S下如何搭建eureka集群

背景 传统应用上云&#xff0c;基于传统应用需要考虑上云的方案和改造成本&#xff0c;这也是传统应用上云过程中的难点&#xff0c;本篇介绍3台eureka搭建的方案。 方案一 此方案借助了K8S中Service的一些功能。 这种方案是传统方案的简单迁移版本&#xff0c;比较易于理解…

深度学习:tf.keras实现模型搭建、模型训练和预测

在sklearn中&#xff0c;模型都是现成的。tf.Keras是一个神经网络库,我们需要根据数据和标签值构建神经网络。神经网络可以发现特征与标签之间的复杂关系。神经网络是一个高度结构化的图&#xff0c;其中包含一个或多个隐藏层。每个隐藏层都包含一个或多个神经元。神经网络有多…

【微信小程序】使用iView组件库中的icons资源

要在微信小程序中使用iView组件库中的icons资源&#xff0c;需要先下载并引入iView组件库&#xff0c;并按照iView的文档进行配置和使用。 以下是一般的使用步骤&#xff1a; 下载iView组件库的源码或使用npm安装iView。 在小程序项目的app.json文件中添加iView组件库的引入配…

mac端好用的多功能音频软件 AVTouchBar for mac 3.0.7

AVTouchBar是来自触摸栏的视听播放器&#xff0c;将跳动笔记的内容带到触摸栏&#xff0c;触摸栏可显示有趣的音频内容&#xff0c;拥有更多乐趣&#xff0c;以一种有趣的方式播放音乐&#xff0c;该软件支持多种音频播放软件&#xff0c;可在Mac上自动更改音乐~ 音频选择-与内…

Flask Bootstrap 导航条

(43条消息) Flask 导航栏&#xff0c;模版渲染多页面_U盘失踪了的博客-CSDN博客 (43条消息) 学习记录&#xff1a;Bootstrap 导航条示例_bootstrap导航栏案例_U盘失踪了的博客-CSDN博客 1&#xff0c;引用Bootstrap css样式&#xff0c;导航栏页面跳转 2&#xff0c;页面两列…

实验五 分支限界法

实验五 分支限界法 01背包问题的分治限界法的实现 剪枝函数 限界函数 1.实验目的 1、理解分支限界法的剪枝搜索策略&#xff0c;掌握分支限界法的算法框架 2、设计并实现问题&#xff0c;掌握分支限界算法。 2.实验环境 java 3.问题描述 给定n种物品和一背包。物品i的重…

Cesium态势标绘专题-位置点(标绘+编辑)

标绘专题介绍:态势标绘专题介绍_总要学点什么的博客-CSDN博客 入口文件:Cesium态势标绘专题-入口_总要学点什么的博客-CSDN博客 辅助文件:Cesium态势标绘专题-辅助文件_总要学点什么的博客-CSDN博客 本专题没有废话,只有代码,代码中涉及到的引入文件方法,从上面三个链…

企业微信在ios机型无法吊起打开个人信息页接口(openUserProfile)

wx.qy.openUserProfile({type: 1,//1表示该userid是企业成员&#xff0c;2表示该userid是外部联系人userid: "wmEQlEGwAAHxbWYDOK5u3Af13xlYAAAA", //可以是企业成员&#xff0c;也可以是外部联系人success: function(res) {// 回调} });遇到的问题&#xff1a;调用打…

动态规划入门第1课

1、从计数到选择 ---- 递推与DP&#xff08;动态规划&#xff09; 2、从递归到记忆 ---- 子问题与去重复运算 3、动态规划的要点 第1题 网格路1(grid1) 小x住在左下角(0,0)处&#xff0c;小y在右上角(n,n)处。小x需要通过一段网格路才能到小y家。每次&#xff0c;小x可以选…

macOS mysql 8.0 忘记密码

╰─➤ mysql -V mysql Ver 8.0.33 for macos13.3 on arm64 (Homebrew)mysql.server status mysql.server stopskip-grant-tables 启动mysql ─➤ /opt…

云计算和云架构是什么 有什么用途?

云计算是一种基于互联网的计算方式&#xff0c;它通过网络将计算资源(如计算能力、存储、网络带宽等)以服务的形式提供给用户&#xff0c;并允许用户根据需求进行灵活的资源调配和管理。云计算通常分为三个层次&#xff0c;即基础设施即服务(IaaS)、平台即服务(PaaS)和软件即服…

【Matlab】基于遗传算法优化 BP 神经网络的时间序列预测(Excel可直接替换数据)

【Matlab】基于遗传算法优化 BP 神经网络的时间序列预测&#xff08;Excel可直接替换数据&#xff09; 1.模型原理2.文件结构3.Excel数据4.分块代码4.1 arithXover.m4.2 delta.m4.3 ga.m4.4 gabpEval.m4.5 initializega.m4.6 maxGenTerm.m4.7 nonUnifMutation.m4.8 normGeomSel…

操作系统笔记、面试八股(三)—— 系统调用与内存管理

文章目录 3. 系统调用3.1 用户态与内核态3.2 系统调用分类3.3 如何从用户态切换到内核态&#xff08;系统调用举例&#xff09; 4. 内存管理4.1 内存管理是做什么的4.1.1 为什么需要虚拟地址空间4.1.2 使用虚拟地址访问内存有什么优势 4.2 常见的内存管理机制4.3 分页管理4.3.1…

kettle开发-Day40-AI分流之case/switch

前言&#xff1a; 前面我们讲到了很多关于数据流的AI方面的介绍&#xff0c;包括自定义组件和算力提升这块的&#xff0c;今天我们来学习一个关于kettle数据分流处理非常重要的组件Switch / Case 。当我们的数据来源于类似日志、csv文件等半结构化数据时&#xff0c;我们需要在…

Vmware+CentOS+KGDB内核双机调试

1.准备两台CentOS系统的vmware虚拟机 其中一台作为调试机&#xff0c;另一台则作为被调试机。如下图&#xff0c;CentOS7.9x64为被调试机&#xff0c;CentOS7.9x64-Debugger为调试机 2.配置串口设备 若虚拟机有串口设备&#xff08;如打印机&#xff09;&#xff0c;需要先删…

黑马 pink h5+css3+移动端前端

网页概念 网页是网站的一页,网页有很多元素组成,包括视频图片文字视频链接等等,以.htm和.html后缀结尾,俗称html文件 HTML 超文本标记语言,描述网页语言,不是编程语言,是标记语言,有标签组成 超文本指的是不光文本,还有图片视频等等标签 常用浏览器 firefox google safari…

LabVIEW基础-lvlib库

文章目录 lvlib库llb库lvlib与llb的区别lvlib常见错误断开vi与库之间的连接 lvlib库 文件-新建-库&#xff0c;创建一个项目库文件。能在项目中创建的文件类型&#xff0c;都可以在库中创建。 在lvlib上右键-添加-文件&#xff0c;将被选中的文件放到lvlib中。被添加进lvlib的…

关于ETL的两种架构(ETL架构和ELT架构)

ETL&#xff0c;是英文 Extract-Transform-Load 的缩写&#xff0c;用来描述将数据从来源端经过抽取&#xff08;extract&#xff09;、转换&#xff08;transform&#xff09;、加载&#xff08;load&#xff09;至目的端的过程。ETL一词较常用在数据仓库&#xff0c;但其对象…