C++资源管理

1.使用资源句柄和RAII(资源获取即初始化)自动管理资源

RAII的理念很简单,你为资源创建一种代理对象。代理的构造函数获取资源,代理的析构函数释放资源。

RAII的中心思想是,这个代理作为一个局部对象,在作用域结束时自动释放资源。

RAII在C++生态系统中被大量使用,RAII的例子有标准模板库的容器、智能指针和锁。容器管理元素,智能指针管理内存,而锁则管理互斥量。

class ResouceGurad
{
public:explicit ResouceGurad(const std::string& resouce) : resouce_(resouce) {std::cout << "Acquire the " << resouce_ << "." << std::endl;}~ResouceGurad() {std::cout << "Release the " << resouce_ << "." << std::endl;}private:std::string resouce_;
};int main()
{std::cout << '\n';ResouceGurad resGuard1{"memoryBlock1"};std::cout << "\nBefore lock scope.\n";{ResouceGurad resGuard2{"memoryBlock2"};}std::cout << "After lock scope.\n";std::cout << '\n';std::cout << "\nBefore try-catch block.\n";try{ResouceGurad resGuard3{"memoryBlock3"};throw std::bad_alloc{};}catch(const std::bad_alloc& e){std::cerr << e.what() << '\n';}std::cout << "\nAfter try-catch block.\n";std::cout << '\n';
}
2.unique_ptr和shared_ptr表示所有权

std::unique_ptr : 独占所有者

std::shared_ptr : 共享所有者

std::weak_ptr : 对std::shared所管理资源的非占有的引用

std::weak_ptr并不是智能指针。它有一个引用,引用指向被std::shared_ptr所管理的对象。它的接口颇为有限,不可以透明地访问底层资源。通过对std::weak_ptr调用其成员函数lock,可以从某个std::weak_ptr创建出一个std::shared_ptr。

auto sharedPtr = std::make_shared<int>(1998);  //引用计数为1
std::weak_ptr<int> weakPtr(sharedPtr);   //引用计数为1
auto sharedPtr2 = weakPtr.lock();   //引用计数为2
 3.除非需要共享所有权,否则能用unique_ptr就别用shared_ptr

当你需要智能指针的时候,应该首选std::unique_ptr,在设计上,std::unique_ptr和原始指针一样快,且一样可以高效利用内存。

这一结论对于std::shared_ptr则不成立。std::shared_ptr需要管理他的引用计数,并且需要分配额外的内存来维护其控制块。为了管理被控制对象的生存期,控制块是必需的。在你需要共享所有权时std::shared_ptr就能大显身手了,这种情况下,只做一次共享资源的分配,反而可以节省内存和时间。

不要为了做拷贝而贪图方便地使用std::shared_ptr.

void takeUniquePtr(std::unique_ptr<int> p) {std::cout << "*unique: " << *p << std::endl;
}int main()
{std::cout << '\n';auto uniquePtr = std::make_unique<int>(2011);takeUniquePtr(std::move(uniquePtr));auto uniquePtr2 = std::make_unique<int>(2014);auto uniquePtr3 = std::make_unique<int>(2017);std::vector<std::unique_ptr<int>> v;v.push_back(std::move(uniquePtr2));v.push_back(std::move(uniquePtr3));v.push_back(std::make_unique<int>(2020));std::cout << '\n';std::for_each(v.begin(), v.end(), [](std::unique_ptr<int>& p) {std::cout << "*unique: " << *p << std::endl;});
}
 4.使用make_shared创建shared_ptr和使用make_unique创建unique_ptr

理由一:异常安全

理由二:只对std::shared_ptr成立

auto sharPtr1 = std::shared_ptr<int>(new int(1998));
auto sharPtr2 = std::shared_ptr<int>(1998);

当你调用std::shared_ptr<int>(new int(1998))时,会发生两次内存分配;一次是针对new int(1998),还有一次是针对std::shared_ptr的控制块。内存分配代价较高,所以尽量避免,std::make_shared<int>(1998)可将两次内存分配变成一次。

5.使用std::weak_ptr来打破shared_ptr形成环

如果std::shared_ptr互相引用,就会形成环状引用。

#include <memory>struct B; // 前向声明struct A {std::shared_ptr<B> b_ptr;
};struct B {std::shared_ptr<A> a_ptr;
};int main() {auto a = std::make_shared<A>();auto b = std::make_shared<B>();a->b_ptr = b;b->a_ptr = a;// 这里 a 和 b 互相引用,导致内存无法释放
}

解决方法:

#include <memory>struct B; // 前向声明struct A {std::shared_ptr<B> b_ptr;
};struct B {std::weak_ptr<A> a_ptr; // 使用 weak_ptr
};int main() {auto a = std::make_shared<A>();auto b = std::make_shared<B>();a->b_ptr = b;b->a_ptr = a;// 现在不会导致内存泄漏
}

6.只在显式表达生存期语义时以智能指针作为参数
以智能指针作为函数参数
函数签名语义
func(std::unique_ptr<Widget>)func拿走所有权
func(std::unique_ptr<Widget>&)func要重装Widget
func(std::shared_ptr<Widget>)func共享所有权
func(std::shared_ptr<Widget>&)func可能要重装Widget
func(const std::shared_ptr<Widget>&)func可能保有一份引用计

7.不要传递从智能指针别名中获得的指针或引用。

📌 什么是 "智能指针别名" ?

std::shared_ptr 中,我们可以使用 operator*operator-> 获取原始指针或引用:

 

cpp

复制编辑

std::shared_ptr<T> ptr = ...; T* raw_ptr = ptr.get(); // 获取原始指针 T& ref = *ptr; // 获取引用

错误地传递这些指针/引用可能会导致生命周期问题!


🚨 不推荐的错误示例

❌ 1. 传递 shared_ptr 获取的裸指针

#include <iostream>#include <memory> 
void process(int* p) 
{   // 接收裸指针 std::cout << "Value: " << *p << std::endl; 
} int main() 
{ std::shared_ptr<int> ptr = std::make_shared<int>(42); process(ptr.get()); // ❌ 传递 get() 返回的裸指针 
}

⚠️ 为什么有问题?

如果 ptrprocess() 执行时已经销毁,p 就会变成悬空指针,导致 未定义行为(UB)


❌ 2. 传递 shared_ptr 获取的引用

#include <iostream>
#include <memory>void modify(int& x) { // 接收引用x += 10;
}int main() {int* raw_ptr = nullptr;{std::shared_ptr<int> ptr = std::make_shared<int>(42);raw_ptr = ptr.get();  // ❌ 获取裸指针modify(*ptr);  // ✅ 此时仍然安全} // `ptr` 离开作用域,控制的对象已被释放modify(*raw_ptr);  // ❌ 使用已释放的对象,UB!
}

⚠️ 为什么有问题?

  • ptr 离开作用域后被销毁raw_ptr 仍然指向已经释放的内存。
  • modify(*raw_ptr); 访问悬空引用,可能导致程序崩溃。

❌ 3. 在 std::shared_ptr 作用域外存储裸指针

#include <iostream>
#include <memory>int* global_ptr = nullptr;void use_global() {std::cout << "Using global pointer: " << *global_ptr << std::endl;  // 可能 UB!
}int main() {{std::shared_ptr<int> ptr = std::make_shared<int>(99);global_ptr = ptr.get();  // ❌ 获取裸指针并存储到全局变量}  // `ptr` 离开作用域,内存被释放use_global();  // ❌ 访问悬空指针,UB!
}

⚠️ 为什么有问题?

  • ptr 释放了对象,但 global_ptr 仍然指向旧的内存,导致 悬空指针 访问。

✅ 推荐的正确做法

✔️ 1. 直接传递 std::shared_ptr

void process(std::shared_ptr<int> p) { std::cout << "Value: " << *p << std::endl;
}int main() {std::shared_ptr<int> ptr = std::make_shared<int>(42);process(ptr);  // ✅ 传递 shared_ptr,确保生命周期
}

✔️ 2. 使用 const std::shared_ptr<T>& 避免额外拷贝

void process(const std::shared_ptr<int>& p) { std::cout << "Value: " << *p << std::endl;
}int main() {std::shared_ptr<int> ptr = std::make_shared<int>(42);process(ptr);  // ✅ 传递引用,避免增加引用计数
}

✔️ 3. 使用 std::weak_ptr 避免循环引用

void process(std::weak_ptr<int> wp) { if (auto p = wp.lock()) { // 检查是否仍然有效std::cout << "Value: " << *p << std::endl;} else {std::cout << "对象已释放!" << std::endl;}
}int main() {std::shared_ptr<int> ptr = std::make_shared<int>(42);process(ptr);  // ✅ 使用 weak_ptr 避免不必要的引用增加
}

📌 总结

做法是否推荐原因
std::shared_ptr<T>&✅ 推荐避免额外拷贝,保证对象有效
const std::shared_ptr<T>&✅ 推荐只读访问,不增加引用计数
std::weak_ptr<T>✅ 推荐避免循环引用,安全访问
传递 ptr.get() 返回的裸指针❌ 不推荐可能导致悬空指针
传递 *ptr 获取的引用❌ 不推荐可能导致悬空引用
在作用域外存储 ptr.get() 的指针❌ 不推荐可能导致未定义行为

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

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

相关文章

动态规划练习八(01背包问题)

一、问题介绍与解题心得 01背包问题就是每个物品数量只有一个&#xff0c;每个物品可以取或不取&#xff0c;来达到收益最大&#xff0c;或者收益在某个值。 限制条件&#xff1a;背包容量有限&#xff0c;物品个数只有1个 解决问题&#xff1a;从价值入手&#xff08;价值最…

Java实习生面试题汇总

Java实习生面试题汇总 简介 本人是二本大三学生&#xff0c;下半年大四。暑假在上海这边找实习工作&#xff0c;面了几家公司&#xff0c;所问到的问题记录在下面。 因为是在校生&#xff0c;没任何实习经历&#xff0c;一般找我面试的都是小公司&#xff0c;一般问的比较简…

开源安全一站式构建!开启企业开源治理新篇章

在如今信息技术日新月异、飞速发展的数字化时代&#xff0c;开源技术如同一股强劲的东风&#xff0c;为企业创新注入了源源不断的活力&#xff0c;然而&#xff0c;正如一枚硬币有正反两面&#xff0c;开源技术的广泛应用亦伴随着不容忽视的挑战。安全风险如影随形&#xff0c;…

xxl-job 自定义告警短信发送

官方介绍 代码实现 实现 JobAlarm 重写 doAlarm 方法 Component public class SmsJobAlarm implements JobAlarm {Overridepublic boolean doAlarm(XxlJobInfo info, XxlJobLog jobLog) {boolean alarmResult true;// 简单内容&#xff0c;根据业务自行修改String template …

大数据学习之Spark分布式计算框架RDD、内核进阶

一.RDD 28.RDD_为什么需要RDD 29.RDD_定义 30.RDD_五大特性总述 31.RDD_五大特性1 32.RDD_五大特性2 33.RDD_五大特性3 34.RDD_五大特性4 35.RDD_五大特性5 36.RDD_五大特性总结 37.RDD_创建概述 38.RDD_并行化创建 演示代码&#xff1a; // 获取当前 RDD 的分区数 Since ( …

【分布式架构理论3】分布式调用(2):API 网关分析

文章目录 一、API 网关的作用1. 业务层面&#xff1a;简化调用复杂性2. 系统层面&#xff1a;屏蔽客户端调用差异3. 其他方面&#xff1a; 二、API 网关的技术原理1. 协议转换2. 链式处理3. 异步请求机制1. Zuul1&#xff1a;同步阻塞处理2. Zuul2&#xff1a;异步非阻塞处理 三…

3.【BUUCTF】XSS-Lab1

进入题目页面如下 好好好&#xff0c;提示点击图片&#xff0c;点进去页面如下&#xff0c;且url中有传参&#xff0c;有注入点 发现题目给出了源码 查看得到本题的源码 分析一下代码 <!DOCTYPE html><!--STATUS OK--> <!-- 声明文档类型为 HTML5&#xff0c;告…

uniapp小程序自定义中间凸起样式底部tabbar

我自己写的自定义的tabbar效果图 废话少说咱们直接上代码&#xff0c;一步一步来 第一步&#xff1a; 找到根目录下的 pages.json 文件&#xff0c;在 tabBar 中把 custom 设置为 true&#xff0c;默认值是 false。list 中设置自定义的相关信息&#xff0c; pagePath&#x…

105,【5】buuctf web [BJDCTF2020]Easy MD5

进入靶场 先输入试试回显 输入的值成了password的内容 查看源码&#xff0c;尝试得到信息 什么也没得到 抓包&#xff0c;看看请求与响应里有什么信息 响应里得到信息 hint: select * from admin where passwordmd5($pass,true) 此时需要绕过MD5&#xff08;&#xff09;函…

JVM监控和管理工具

基础故障处理工具 jps jps(JVM Process Status Tool)&#xff1a;Java虚拟机进程状态工具 功能 1&#xff1a;列出正在运行的虚拟机进程 2&#xff1a;显示虚拟机执行主类(main()方法所在的类) 3&#xff1a;显示进程ID(PID&#xff0c;Process Identifier) 命令格式 jps […

【大模型】AI 辅助编程操作实战使用详解

目录 一、前言 二、AI 编程介绍 2.1 AI 编程是什么 2.1.1 为什么需要AI辅助编程 2.2 AI 编程主要特点 2.3 AI编程底层核心技术 2.4 AI 编程核心应用场景 三、AI 代码辅助编程解决方案 3.1 AI 大模型平台 3.1.1 AI大模型平台代码生成优缺点 3.2 AI 编码插件 3.3 AI 编…

机器学习--2.多元线性回归

多元线性回归 1、基本概念 1.1、连续值 1.2、离散值 1.3、简单线性回归 1.4、最优解 1.5、多元线性回归 2、正规方程 2.1、最小二乘法 2.2、多元一次方程举例 2.3、矩阵转置公式与求导公式 2.4、推导正规方程0的解 2.5、凸函数判定 成年人最大的自律就是&#xff1a…

2025最新软件测试面试大全(附答案+文档)

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 1、问&#xff1a;你在测试中发现了一个bug&#xff0c;但是开发经理认为这不是一个bug&#xff0c;你应该怎样解决? 首先&#xff0c;将问题提交到缺陷管理库里…

手写MVVM框架-环境搭建

项目使用 webpack 进行进行构建&#xff0c;初始化步骤如下: 1.创建npm项目执行npm init 一直下一步就行 2.安装webpack、webpack-cli、webpack-dev-server&#xff0c;html-webpack-plugin npm i -D webpack webpack-cli webpack-dev-server html-webpack-plugin 3.配置webpac…

如何自定义软件安装路径及Scoop包管理器使用全攻略

如何自定义软件安装路径及Scoop包管理器使用全攻略 一、为什么无法通过WingetUI自定义安装路径&#xff1f; 问题背景&#xff1a; WingetUI是Windows包管理器Winget的图形化工具&#xff0c;但无法直接修改软件的默认安装路径。原因如下&#xff1a; Winget设计限制&#xf…

数据结构实战之线性表(三)

目录 1.顺序表释放 2.顺序表增加空间 3.合并顺序表 4.线性表之链表实现 1.项目结构以及初始代码 2.初始化链表(不带头结点) 3.链表尾部插入数据并显示 4.链表头部插入数据 5.初始化链表&#xff08;带头结点&#xff09; 6.带头结点的链表头部插入数据并显示 7.带头结…

5.6 Mybatis代码生成器Mybatis Generator (MBG)实战详解

文章目录 前言一、Mybatis Generator简介二、Maven插件运行方式三、生成配置 generatorConfig.xml MyBatis3Simple风格MyBatis3风格MyBatis3DynamicSql风格 四、Java代码运行方式五、MGB生成全部表六、增加Ext包七、Git提交总结 前言 本文我们主要实战Mybatis官方的代码生成器…

DeepSeek:全栈开发者视角下的AI革命者

目录​​​​​​​ DeepSeek&#xff1a;全栈开发者视角下的AI革命者 写在前面 一、DeepSeek的诞生与定位 二、DeepSeek技术架构的颠覆性突破 1、解构算力霸权&#xff1a;从MoE架构到内存革命 2、多模态扩展的技术纵深 3、算法范式的升维重构 4、重构AI竞争规则 三、…

(篇一)基于PyDracula搭建一个深度学习的界面之添加启动界面

文章目录 基于PyDracula搭建一个深度学习的界面插入一个启动界面1启动页面的资源如何加载与管理&#xff1f;2启动界面的代码如何写&#xff1f; 基于PyDracula搭建一个深度学习的界面 插入一个启动界面 1启动页面的资源如何加载与管理&#xff1f; 1. 问题一 启动界面包含一…

无人机图传模块 wfb-ng openipc-fpv,4G

openipc 的定位是为各种模块提供底层的驱动和linux最小系统&#xff0c;openipc 是采用buildroot系统编译而成&#xff0c;因此二次开发能力有点麻烦。为啥openipc 会用于无人机图传呢&#xff1f;因为openipc可以将现有的网络摄像头ip-camera模块直接利用起来&#xff0c;从而…