关于C++智能指针复习总结

RAII(Resource Acquisition Is Initialization): 资源获得即初始化

利用对象生命周期来控制程序的资源(将资源交给对象处理) 智能指针利用了该思想

  • 将资源交给一个对象, 初始化资源(可以是指针或者等等资源), 释放交给析构函数
    因为析构函数无论是什么场景, 对象销毁时一定会被调用, 故此资源一定会被释放

智能指针

使用了RAII的思想管理动态内存分配的一个工具,以减少内存泄露的风险。
在使用上像一个普通指针一样操作(*和->)

头文件<memory.h>

auto_ptr(C++11及以后已被弃用)

  • 拷贝→ 资源管理权转移(会导致原拷贝对象悬空)
    即将原先的智能指针内容转换到新的智能指针内
  • 该智能指针属于一个反面教材
template<typename T>  
class auto_ptr {  T* ptr;  
public:  explicit auto_ptr(T* p = nullptr) : ptr(p) {}  ~auto_ptr() { delete ptr; }  auto_ptr(const auto_ptr& other) : ptr(other.ptr) {  other.ptr = nullptr; // 所有权转移!这是有问题的部分。  }  auto_ptr& operator=(const auto_ptr& other) {  if (this != &other) {  delete ptr; // 删除当前对象所拥有的资源  ptr = other.ptr; // 获取新资源的所有权  other.ptr = nullptr; // 将原所有者的指针设为 nullptr  }  return *this;  }  T& operator*() const { return *ptr; }  T* operator->() const { return ptr; }  
};

unique_ptr

一个智能指针只能管理一个指针对象(不支持赋值和拷贝)

  • 从类的语法角度将拷贝构造和赋值用delete关键字删除, 使得无法复制
  • 并没有从根上解决auto_ptr的问题
template<typename T>  
class unique_ptr {  T* ptr;  
public:  unique_ptr(T* p = nullptr) : ptr(p) {}  ~unique_ptr() { delete ptr; }  // 手动禁止拷贝和赋值unique_ptr(const unique_ptr& other) = delete;  unique_ptr& operator=(const unique_ptr& other) = delete;  // 移动构造和赋值unique_ptr(unique_ptr&& other) noexcept : ptr(other.ptr) {  other.ptr = nullptr;  }  unique_ptr& operator=(unique_ptr&& other) noexcept {  if (this != &other) {  delete ptr;  ptr = other.ptr;  other.ptr = nullptr;  }  return *this;  }  T& operator*() const { return *ptr; }  T* operator->() const { return ptr; }  explicit operator bool() const { return ptr != nullptr; }  
};

shared_ptr

可以表示用多个智能指针管理同一个指针对象(支持赋值和拷贝)

  • 使用引用计数来实现

    • 每个对象生成时计数++
    • 对象释放时计数–
    • 计数为0时则释放所管理的资源
  • 实现shared_ptr

    • 不能使用静态成员变量, 如此静态成员变量是属于整个类, 所有的智能指针对象都会共享该计数(我们应该不同的智能指针对象拥有不同的计数)

    • 应在shared_ptr中添加一个计数指针(指针保存的为地址, 可以在不同对象内传递)在构造中初始化为1, 拷贝时将计数也拷贝+1,在析构时将对应的计数指针–即可

    template<typename T>  
    class shared_ptr {  T* ptr;  int* count;  
    public:  shared_ptr(T* p = nullptr) : ptr(p), count(new int(1)) {}  shared_ptr(const shared_ptr& other) : ptr(other.ptr), count(other.count) {  ++(*count);  }  ~shared_ptr() {  if (--(*count) == 0) {  delete ptr;  delete count;  }  }  shared_ptr& operator=(const shared_ptr& other) {  if (this != &other) {  if (--(*count) == 0) {  delete ptr;  delete count;  }  ptr = other.ptr;  count = other.count;  ++(*count);  }  return *this;  }  T& operator*() const { return *ptr; }  T* operator->() const { return ptr; }  
    };
    

shared_ptr的问题

  • 循环引用(无法在内部解决), 需要使用weak_ptr解决
    weak_ptr主要用shared_ptr构造, 用于解决循环引用问题

    1. 当一个自定义类型内也存在多个shared_ptr时, 两个及以上个自定义类型通过shared_ptr互相指向时则会出现循环引用的问题
    2. weak_ptr不会增加对应shared_ptr的引用计数(即不参与资源管理)
      但是仍旧可以像指针一样使用weak_ptr(可以访问和修改资源)
    struct Node
    {int _val;//成员包含shared_ptr类型, 去管理其它对象时会对应增加计数/*std::shared_ptr<Node> _next;std::shared_ptr<Node> _prev;*///管理其它对象时不会增加对应计数std::weak_ptr<Node> _next;std::weak_ptr<Node> _prev;~Node(){cout << "~Node" << endl;}
    };// 循环引用 -- weak_ptr不是常规智能指针,没有RAII,不支持直接管理资源
    // weak_ptr主要用shared_ptr构造,用来解决shared_ptr循环引用问题
    void test_shared_ptr2()
    {/*std::shared_ptr<Node> n1(new Node);std::shared_ptr<Node> n2(new Node);//通过成员进行了相互引用, 则彼此"缠绕"了起来//n1的_next释放需要依赖指针类型的析构, 结点的析构需要依赖所在对象的析构, 但所在对象的析构又依赖于n2的_prev的析构(当_prev析构了, node1才会被析构); 反之类似, 故彼此缠绕, 形成循环引用n1->_next = n2;//增加了node2的计数n2->_prev = n1;//增加了node1的计数*/std::shared_ptr<Node> n1(new Node);std::shared_ptr<Node> n2(new Node);cout << n1.use_count() << endl;cout << n2.use_count() << endl;n1->_next = n2;n2->_prev = n1;cout << n1.use_count() << endl;cout << n2.use_count() << endl;
    }
    

weak_ptr

一般都作为shared_ptr的辅助指针, 解决其循环引用的问题, 它只会指向资源, 而不参与管理资源(即不会增加引用计数)

删除器

关于new和new[]生成的指针对应delete和delete[]的问题(是否匹配使用, 释放空间)

  • 为什么存在删除器 ?
    • new和new[]底层都是调用了operator new函数, 然后调用了malloc以及对应对象的构造函数,但区别在于new[]在malloc时多malloc了4个字节存在首部(用于delete[]时对应找到malloc了多少个对象空间)
    • delete和delete[]底层都是调用了operator delete, 然后调用了对应对象的析构和free,区别在于delete调用了一次析构和一次free, 但delete[]调用了n次析构和1次free
      但在析构时,指针会往前偏移4个字节找到总构造的对象个数所对应的空间, 然后依次调用析构和free
    • 如果是new[]出来的对象使用delete释放时, 由于delete不会进行偏移, 会导致free的位置不正确, 导致程序崩溃
  • 定制删除器
    • 仿函数
    • lambda表达式
    • 不同的智能指针对应的删除器
      • shared_ptr可在构造函数内传入仿函数或者lambda表达式
      • unique_ptr只能从模板参数传入

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

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

相关文章

QT C++ 模型视图结构 QTableView 简单例子

在Qt中&#xff0c;MVC模式被广泛使用于各种用户界面框架中&#xff0c;包括Qt的模型视图结构。Qt的模型视图结构是基于MVC模式设计的&#xff0c;其中包括了Model、View和Delegate三个部分。 QTableView是Qt模型视图结构中的一种视图&#xff0c;它用于以表格形式显示数据。 …

【No More Room in Hell】地狱已满服务器一键开服多人联机教程

1、购买后登录服务器 进入控制面板后会出现正在安装的界面&#xff0c;安装大约5分钟&#xff08;如长时间处于安装中请联系我们的客服人员&#xff09; 2、连接游戏 2.1、安装完成后复制开机下方的IP地址 2.2、打开Steam点击左上角的查看➡服务器 点击收藏➡右下角号 粘贴地址…

记一次由于代码原因导致Mysql连接被打满和唯一索引重复问题

先说一下事情产生的背景&#xff1a;原先的代码逻辑是消费MQ&#xff0c;然后请求其他服务的接口&#xff0c;对接口的返回值result做落库操作&#xff0c;现在要新加个逻辑&#xff0c;做完落库操作后还要再将result封装落到新表中&#xff1b;即消费一次MQ(MQ消息的频率非常高…

解决npm卡死,无法安装依赖

npm卡死&#xff0c;无法安装依赖 异常描述原因分析与解决方法 异常描述 1.无法进入命令行&#xff0c;或是很慢没反应 2.装表格无限滚动的el-table-infinite-scroll依赖一上午了&#xff0c;也不能装&#xff0c;报错提示 原因分析与解决方法 1.命令行的问题&#xff1a;缓…

RabbitMQ 之 死信队列

目录 ​编辑一、死信的概念 二、死信的来源 三、死信实战 1、代码架构图 2、消息 TTL 过期 &#xff08;1&#xff09;消费者 &#xff08;2&#xff09;生产者 &#xff08;3&#xff09;结果展示​编辑 3、队列达到最大长度 &#xff08;1&#xff09;消费者 &…

AI智能体研发之路-模型篇(五):pytorch vs tensorflow框架DNN网络结构源码级对比

博客导读&#xff1a; 《AI—工程篇》 AI智能体研发之路-工程篇&#xff08;一&#xff09;&#xff1a;Docker助力AI智能体开发提效 AI智能体研发之路-工程篇&#xff08;二&#xff09;&#xff1a;Dify智能体开发平台一键部署 AI智能体研发之路-工程篇&#xff08;三&am…

3DF Zephyr v7 解锁版安装教程 (照片转三维模型软件)

前言 3DF Zephyr是一款照片转三维模型软件&#xff0c;可以导出许多常见的3D格式&#xff0c;甚至无需外部工具即可生成无损视频。此外&#xff0c;可以生成真正的正射影像&#xff0c;数字高程模型&#xff08;DTM&#xff09;&#xff0c;甚至可以计算面积&#xff0c;体积&…

软件技术架构全面详解

软件架构全面详解 软件架构 这个与建筑设计架构类似,建筑设计架构师负责设计建筑物的整体结构、布局和功能分配。 而软件架构师,负责设计软件系统的整体组织结构、模块划分、和功能分配。 两者都需要考虑到业务功能、性能、可扩展性、安全性、以及用户体验等方面。 软件架…

TypeScript 函数

函数基础 在 TypeScript 中&#xff0c;函数的定义方式与 JavaScript 类似&#xff0c;但是可以添加类型注解来增强函数的类型安全性。例如&#xff1a; function greet(name: string): string {return Hello, ${name}!; }上面的例子定义了一个名为 greet 的函数&#xff0c;…

【深度学习】ultralytics, yolo seg,实例分割图绘制,核对yolo seg 的txt标记对不对

这段代码的作用是从指定路径读取图像和标签文件&#xff0c;然后在图像上绘制分割区域和相关点&#xff0c;并保存最终的图像。以下是每个函数的具体作用及其解释&#xff1a; read_labels(label_path): 读取指定路径的标签文件。标签文件的每一行表示一个物体的分割信息&#…

【python深度学习】——torch.expand()广播机制|torch.norm()

【python深度学习】——torch.expand广播机制|torch.norm 1. torch.expand()与广播机制2. torch.norm()计算范数3. 结合使用的完整示例代码 1. torch.expand()与广播机制 在处理3D点云时, 有时需要对两帧点云进行逐点的三维坐标相加减、做点积等运算, 但是读入的PCD文件中,点云…

硬盘的分区

目录 概念 硬盘的分区 实操 创建分区 fdisk&#xff08;<2T&#xff09; 创建文件系统 挂载 自动挂载&#xff08;永久挂载&#xff09; gpt区分 swap 交换分区 如何删除已挂载的分区 概念 硬盘&#xff1a;计算机的存储设备。&#xff08;如无特殊说明&#xff0…

sklearn线性回归--岭回归

sklearn线性回归--岭回归 岭回归也是一种用于回归的线性模型&#xff0c;因此它的预测公式与普通最小二乘法相同。但在岭回归中&#xff0c;对系数&#xff08;w&#xff09;的选择不仅要在训练数据上得到好的预测结果&#xff0c;而且还要拟合附加约束&#xff0c;使系数尽量小…

基于springboot+vue的班级综合测评管理系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

操作系统 实验18 批处理操作接口8:函数

1、建立文件func2.sh&#xff0c;输出文件内容各行及行数 脚本&#xff1a; #!/bin/bash echo -n "请输入一个文件名及路径&#xff1a;" read FILE statisfile(){local i0while read linedo let iecho "$i $line"done < $FILEecho "$FILE有$i行…

高德地图之获取经纬度并且根据获取经纬度渲染到路线规划

话不多说&#xff0c;直接给兄弟们上干货&#xff0c;修改了好多次&#xff0c;之前都是获取到路线但是无法删除&#xff0c;这次是根据点击的单双判断开始和结束 <!DOCTYPE html> <html><head><meta charset"utf-8"><meta http-equiv&q…

解读 MySQL 容器信息:`docker inspect` 字段详解

前言 在使用 Docker 时&#xff0c;docker inspect 命令是一个非常有用的工具&#xff0c;它能够返回容器或镜像的详细配置信息和状态。以下是对 docker inspect mysql 命令输出的字段的详细解释&#xff0c;这些信息可以帮助您更好地了解容器的内部工作机制。 容器基础信息 …

回顾Java中的算术运算符、关系运算符和逻辑运算符,并解释下和的区别

一、在Java中&#xff0c;存在多种类型的运算符&#xff0c;包括算术运算符、关系运算符和逻辑运算符。以下是这些运算符的列表和简要说明&#xff1a; 算术运算符 加法运算符 (): 用于将两个操作数相加。减法运算符 (-): 用于从一个操作数中减去另一个操作数。乘法运算符 (*…

【前端】CSS弹性布局 display-flex

一、display-flex弹性布局 Flex&#xff0c;用来为盒装模型提供最大的灵活性。任何一个容器都可以指定为Flex布局。 在父DIV中使用 display: -webkit-flex; /*在webkit内核的浏览器上使用要加前缀*/ display: flex; //将对象作为弹性伸缩盒显示// 沿水平主轴让元素从左向右排…

git中忽略文件的配置

git中忽略文件的配置 一、在项目根目录下创建.gitignore文件二、配置规则如果在配置之前已经提交过文件了&#xff0c;要删除提交过的&#xff0c;如何修改&#xff0c;参考下面的 一、在项目根目录下创建.gitignore文件 .DS_Store node_modules/ /dist# local env files .env…