Effective C++(四): 资源管理

文章目录

  • 一、智能指针驱动的RAII
  • 二、shared_ptr 和 weak_ptr
  • 三、如何复制 RAII 对象
  • 四、在资源管理类中应该提供对原始资源的访问函数


为了防止忘记调用 delete 造成的内存泄露,我们应该尽可能让对象管理资源,并且采用 RAII 机制(Resource Acquisition is Initialize)机制,让析构函数负责资源的释放。

一、智能指针驱动的RAII

在cpp11中,可以使用unique_ptr 或者 shared_ptr两种智能指针来管理内存。其中 unique_ptr 通过专一所有权来管理 RAII 的对象,而shared_ptr通过引用计数来管理。

std::unique_ptr pUniqueInv1(CreateInvestment());
std::unique_ptr pUniqueInv2(std::move(pUniqueInv1));
std::shared_ptr pSharedInv1(CreateInvestment());

std::shared_ptr pSharedInv2(pSharedInv1); /
std::shared_ptr pSharedInv2(std::move(pSharedInv1))

std::move(pSharedInv1)返回的是pSharedInv1的右值引用,也就是一个std::shared_ptr&&类型,在执行完这句之后,pSharedInv1就变成了一个空指针 nullptr,而pSharedInv2现在拥有原本属于pSharedInv1 的对象。 请注意在调用std::shared_ptr 的移动构造函数的时候,shared_ptr的引用技术不变。

智能指针默认会自动 delete 所持有的对象,我们也可以为智能指针指定所管理对象的释放方式(删除器deleter):

// void GetRidOfInvestment(Investment*) {}

std::unique_ptr<Investment, decltype(GetRidOfInvestment)*> pUniqueInv(CreateInvestment(), GetRidOfInvestment);
std::shared_ptr pSharedInv(CreateInvestment(), GetRidOfInvestment);

在这里decltype的作用是:

二、shared_ptr 和 weak_ptr

一个很常见的面试问题是能否使用 weak_ptr来实现 RAII ? 答案显然是否定的。 首先介绍一下weak_ptr, weak_ptr是一种用于解决 shared_ptr的循环计数死锁的智能指针。一个例子如下:

#include <memory>
#include <vector>class Child;class Parent {
public:std::vector<std::shared_ptr<Child>> children;void addChild(const std::shared_ptr<Child>& child) {children.push_back(child);}~Parent() {// 析构函数}
};class Child {
public:std::shared_ptr<Parent> parent;Child(const std::shared_ptr<Parent>& p) : parent(p) {}~Child() {// 析构函数}
};

可以看到 child 和 parent 互相持有对方的shared_ptr, 造成循环引用。 哪怕当这些对象超出作用御的时候他们的析构函数也不会被调用,从而导致内存泄露。

为了解决这个问题,我们可以使用 weak_ptr 解决循环引用,我们可以把 Child 中的shared_ptr 改成 weak_ptr :

class Child {
public:std::weak_ptr<Parent> parent; // 使用 weak_ptr 而非 shared_ptrChild(const std::shared_ptr<Parent>& p) : parent(p) {} //这里参数仍然是shapred_ptr 因为 1. shared_ptr => weak_ptr 转换是兼容的。 2.确保有一个 shared_ptr<Parent>存在保证 Parent 对象存活。~Child() {// 析构函数}
};

三、如何复制 RAII 对象

  1. 引用计数
    正如 shared_ptr一样,对于每一个资源对象的每一次复制就让引用计数 + 1, 每一个对象离开定义域调用析构函数使得引用计数 - 1, 直到引用计数为 0 就把资源销毁。

  2. 深拷贝
    在拷贝的时候不但copy 对象,同时 copy 底层资源,比如:
    请注意,在拷贝的时候,有三点是非常需要注意的:
    1.是否拷贝了底层资源对象
    2.是否 handle 了自赋值
    3.是否 handle 了异常处理

#include<iostream>
#include<cstring>class RAIIArray { 
public:RAIIArray(const char* str) {if(str) {len_ = std::strlen(str) + 1;data_ = new char[len];strcpy(data_, str);} else {data_ = nullptr;len_ = 0;}}//Deepcopy for Copy Constructor RAIIArray(const RAIIArray& other) { size_ = other.size_;if(size_ > 0) {data_ = new char[size_];std::strcpy(data_, other.data_);}  else { data_ = nullptr;}}//Deepcopy for operator= RAIIArray& operator=(const RAIIArray& other) { //Identity Judgementif(this != &other) {delete data;size = other.size;if(size > 0) {data = new char[size];std::strcpy(data, other.data); } else {data = nullptr;size = 0;}} return *this;}//You can also use this : first explicit call copy constructor then copy & swapRAIIArray& operator=(const RAIIArray& other) { if(this != other) {RAIIArray temp(other); //显shi 调用了拷贝构造函数swap(*this, other);}return *this;}//use friend to access the private variables in first and second friend void swap( RAIIArray& first,  RAIIArray& second) {using std::swap;swap(first.size, second.size);swap(first.data, second.data);}//Deepcopy with copy-and-swapRAIIArray& operator=(RAIIArray other) { swap(*this,other);return *this;}}
  1. 转移底层资源所有权
    和std::unique_ptr类似,永远保持只有一个对象拥有对资源的管理权,当需要复制对象的时候转移资源的管理权。

四、在资源管理类中应该提供对原始资源的访问函数

和所有的智能指针一样,stl 中的智能指针也提供了对原始资源的隐藏访问和显示访问
Investment* pRaw = pSharedInv.get(); // 显示访问
Investment raw = *pSharedInv; //隐式访问
这里,pSharedInv是一个 shared_ptr, 指向Investment类型

当我们自己在设计自己的资源管理类的时候,也要考虑在提供原始资源访问的同时,是使用显示还是隐式方法。
比如:
class Font


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

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

相关文章

Sql Server数据库跨机器完整恢复(源文件恢复)

问题描述 在操作系统异常的情况下&#xff0c;SQL Server 和相关的业务系统遭受了不可用的情况。由于操作系统问题&#xff0c;导致旧服务器无法正常运行。为了恢复业务功能并确保数据完整性&#xff0c;采取了以下步骤来在新机器上进行 SQL Server 的重新安装和数据恢复。 面…

【超全】React学习笔记 中:进阶语法与原理机制

React学习笔记 React系列笔记学习 上篇笔记地址&#xff1a;【超全】React学习笔记 上&#xff1a;基础使用与脚手架 下篇笔记地址&#xff1a;【超全】React学习笔记 下&#xff1a;路由与Redux状态管理 React进阶组件概念与使用 1. React 组件进阶导读 在掌握了 React 的基…

Verilog 入门(七)(任务、函数)

文章目录 任务任务定义任务调用 函数函数说明部分函数调用 值变转储文件 任务 一个任务就像一个过程&#xff0c;它可以从描述的不同位置执行共同的代码段。共同的代码段用任务定义编写成任务&#xff0c;这样它就能够从设计描述的不同位置通过任务调用被调用。任务可以包含时…

actual combat 24 —— 创建数据库表定义码值字段时,tinyint类型和varchar(2)该如何选择?

tinyint&#xff1a;最大数值为127&#xff0c;占用1字节空间varchar(2)&#xff1a;最大数值为99&#xff0c;占用2字节空间&#xff0c;但当存储的数字为个位数时varchar类型是可变的&#xff0c;只占用1个字节空间。 从这个角度来看的话&#xff0c;用tinyint类型来存储码值…

前端开发神器之 VsCode AI 辅助插件 DevChat

目录 前言DevChat介绍DevChat 独特优势注册账号安装插件设置密钥访问指令AI 解疑 最后 #AI编程助手哪家好&#xff1f;DevChat“真”好用 # 前言 我们都有过写代码时反复看了半天也不知道bug在哪&#xff0c;大大浪费了时间。一些基础的代码可能看一会儿能够解决&#xff0c;但…

k8s(三): 基本概念-ReplicaSet与Deployment

PeplicaSet ReplicaSet 的目的是维护一组在任何时候都处于运行状态的 Pod 副本的稳定集合&#xff0c;通常用来保证给定数量的、完全相同的 Pod 的可用性。 最佳实践 Deployment 是一个可以拥有 ReplicaSet 并使用声明式方式在服务器端完成对 Pod 滚动更新的对象。 尽管 Rep…

oracle sql相关语法

SQL*PLUS 在SQL*PLUS执行&#xff0c;会在执行后显示查询的执行计划和统计信息 SET AUTOTRACE ON;SELECT * FROM your_table WHERE column_name value;SET AUTOTRACE OFF;PLSQL PLSQL查询sql界面&#xff0c;鼠标右键&#xff0c;点击执行计划&#xff0c;会出现sql的执行计…

matlab 汽车单车模型固定点跟踪算法

1、内容简介 略 29-可以交流、咨询、答疑 2、内容说明 单车模型固定点跟踪算法 单车模型&#xff0c;固定点跟踪算法&#xff0c;动画演示&#xff0c; 汽车单车模型、转弯动画、固定点跟踪算法、pid控制 3、仿真分析 略 A[0,5;0,0];B[0;1]; Q10*eye(2);R1; Klqr(A…

Java高级技术-反射

认识反射、获取类 获取类的方法 获取类的构造器 获取类的构造器、并对其进行操作 获取构造器的作用&#xff1a;依然是初始化对象返回 获取成员变量 获取成员变量的方法 获取成员变量的作用&#xff1a;赋值、取值 获取类的成员方法 方法 作用&#xff1a;依然是执行 作用、…

webGL开发学科演示项目方案

开发学科演示项目需要考虑到教育目标、互动性和用户体验。以下是一个可能的技术方案&#xff0c;可用于实现这样的项目&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1.WebGL 框架&#xff1a; 选择…

golang 函数选项模式

一 什么是函数选项模式 函数选项模式允许你使用接受零个或多个函数作为参数的可变构造函数来构建复杂结构。我们将这些函数称为选项&#xff0c;由此得名函数选项模式。 例子&#xff1a; 有业务实体Animal结构体&#xff0c;构造函数NewAnimal&#xff08;&#xff09;&…

【超全】React学习笔记 上:基础使用与脚手架

React学习笔记 React系列笔记学习 中篇笔记地址&#xff1a;【超全】React学习笔记 中&#xff1a;进阶语法与原理机制 下篇笔记地址&#xff1a;【超全】React学习笔记 下&#xff1a;路由与Redux状态管理 React 简介 React 是一个由 Facebook 开发并维护的用于构建用户界面的…

C++的一些基础

1、reinterpret_cast reinterpret_cast是四种强制转换中功能最为强大的&#xff08;最暴力&#xff0c;最底层&#xff0c;最不安全&#xff09;。它的本质是编译器的指令。 它的作用&#xff1a;它可以把一个指针转换成一个整数&#xff0c;也可以把一个整数转换成一个指针。…

网络运维与网络安全 学习笔记2023.12.2

网络运维与网络安全 学习笔记 第三十三天 今日目标 Linux系统综述、部署本地Linux、配置Linux网络 SSH远程控制、远程文档管理、选购ECS云主机 Linux系统综述 Linux是一种操作系统 Linux之父&#xff0c;Linus Torwalds 1991年10月&#xff0c;发布0.02版&#xff08;第一…

Redis集群详解

1.1 什么是Redis集群 Redis集群是一种通过将多个Redis节点连接在一起以实现高可用性、数据分片和负载均衡的技术。它允许Redis在不同节点上同时提供服务&#xff0c;提高整体性能和可靠性。根据搭建的方式和集群的特性&#xff0c;Redis集群主要有三种模式&#xff1a;主从复制…

海林猴头菇 区域公用品牌形象正式发布

猴头菇是中国八大“山珍”之一&#xff0c;自古就有“山珍猴头&#xff0c;海味燕窝”之说&#xff0c;猴头菇在中国既是食用珍品&#xff0c;又是重要的药用菌。 海林市位于黑龙江省东南部&#xff0c;地处长白山脉张广才岭东麓&#xff0c;素有“林海雪原”之称。 海林猴头菇…

zookeeper集群+kaafka集群

kafka3.0之前依赖于zookeeper zookeeper开源&#xff0c;分布式的架构&#xff0c;提供协调服务&#xff08;Apache项目&#xff09; 基于观察者模式涉及的分布式服务管理架构 存储和管理数据&#xff0c;分布式节点上的服务接受观察者的注册&#xff0c;一旦分布式节点上的…

计算机网络TCP篇②

一、TCP 重传、滑动窗口、流量控制、拥塞控制 1.1、重传机制 在 TCP 中&#xff0c;当发送端的数据达到接受主机时&#xff0c;接收端主机会返回一个确认应答消息&#xff0c;表示已收到消息。但是在复杂的网络中&#xff0c;并一定能顺利正常的进行数据传输&#xff0c;&…

Linux 匿名页反向映射

1. 何为反向映射 正向映射&#xff1a; 用户进程在申请内存时&#xff0c;内核并不会立刻给其分配物理内存&#xff0c;而是先为其分配一段虚拟地址空间&#xff0c;当进程访问该虚拟地址空间时&#xff0c;触发page fault异常&#xff0c;异常处理流程中会为其分配物理页面&am…

关于Typora如何插入自己的云端视频的方法

关于Typora如何插入自己的云端视频的方法 文章目录 关于Typora如何插入自己的云端视频的方法前言&#xff1a;实现步骤&#xff1a;小结 前言&#xff1a; 我本来使用gitee来作为typora的图床&#xff0c;但我现在想要把我自己的视频上传到云端&#xff0c;然后通过超链接在ty…