深入理解 C++11 智能指针:独占、共享与弱引用的完美管理
文章目录
- std::unique_ptr(独占式智能指针)
- std::shared_ptr(共享式智能指针)
- std::weak_ptr(弱引用智能指针)
- 示例展示:
- 智能指针的原理
- 内存泄漏
- **什么是内存泄漏,内存泄漏的危害**
- **如何检测内存泄漏**
- **如何避免内存泄漏**
- C++11和boost中智能指针的关系
- unique_ptr自己实现
- shared_ptr
- C++标准库提供了三种主要类型的智能指针:
std::unique_ptr(独占式智能指针)
unique_ptr是最常用的一种智能指针,它确保一个指针在同一时刻只能有一个所有者。当unique_ptr超出作用域时,它所持有的资源会自动被销毁。
特点:
- 不能被复制,只能被转移(通过std::move)。
- 负责管理指向对象的生命周期,自动释放资源。
std::unique_ptr<int> ptr(new int(5)); // 创建unique_ptr并指向动态分配的内存
// 当ptr超出作用域时,内存会被自动释放
适用场景:
- 用于表达资源的独占所有权,比如文件句柄、数据库连接等。
std::shared_ptr(共享式智能指针)
shared_ptr允许多个指针共享对同一个对象的所有权。当所有指向同一对象的shared_ptr都被销毁时,所管理的资源才会被释放。shared_ptr通过引用计数来管理对象的生命周期。
特点:
- 支持多个shared_ptr实例共同拥有一个对象。
- 引用计数:每当一个新的shared_ptr指向对象时,引用计数增加;当shared_ptr销毁时,引用计数减少;当引用计数为0时,资源被自动释放。
std::shared_ptr<int> ptr1 = std::make_shared<int>(10); // 创建shared_ptr并初始化
std::shared_ptr<int> ptr2 = ptr1; // 共享资源,引用计数为2
适用场景:
- 用于需要多个所有者共享一个资源的场景,比如共享的数据结构、图形界面中多个组件共享同一数据等。

std::weak_ptr(弱引用智能指针)
weak_ptr是与shared_ptr配合使用的智能指针,它不会增加引用计数,因此不会影响资源的生命周期。weak_ptr通常用于解决shared_ptr之间的循环引用问题。
特点:
- 不增加引用计数,因此不会导致资源无法释放。
- 可以通过 lock() 方法将其转换为shared_ptr,在获取资源之前,需要确保资源还存在。
std::weak_ptr<int> weakPtr = ptr1; // 不增加引用计数
if (auto lockedPtr = weakPtr.lock())
{// 使用lockedPtr来访问资源
}
适用场景:
- 用于观察者模式,避免循环引用。
- 用于缓存系统,避免过度占用内存。

智能指针类型概览
-
unique_ptr:独占所有权,不能拷贝只能移动
-
shared_ptr:共享所有权,基于引用计数
-
weak_ptr:配合 shared_ptr 使用,解决循环引用问题
-
auto_ptr(已废弃):C++98 的智能指针,不推荐使用
示例展示:
unique_ptr 示例(独占所有权)
#include <memory>
#include <iostream>class MyClass {
public:MyClass() { std::cout << "MyClass created\n"; }~MyClass() { std::cout << "MyClass destroyed\n"; }void greet() { std::cout << "Hello from MyClass!\n"; }
};int main()
{// 创建 unique_ptrstd::unique_ptr<MyClass> ptr1 = std::make_unique<MyClass>();// 转移所有权std::unique_ptr<MyClass> ptr2 = std::move(ptr1);if(ptr2) {ptr2->greet(); // 正常调用}if(!ptr1) {std::cout << "ptr1 is now empty\n";}// 自动释放内存,无需手动deletereturn 0;
}
shared_ptr 示例(共享所有权)
#include <memory>
#include <iostream>class Item
{
public:Item() { std::cout << "Item created\n"; }~Item() { std::cout << "Item destroyed\n"; }
};int main()
{// 创建共享指针std::shared_ptr<Item> ptr1 = std::make_shared<Item>();{std::shared_ptr<Item> ptr2 = ptr1; // 引用计数变为2std::cout << "Use count: " << ptr2.use_count() << "\n"; // 输出2} // 引用计数回到1std::cout << "Use count: " << ptr1.use_count() << "\n"; // 输出1// 离开作用域时引用计数归零,自动释放内存return 0;
}
weak_ptr 示例(解决循环引用)
#include <memory>
#include <iostream>class Node;class Parent
{
public:std::shared_ptr<Node> child;~Parent() { std::cout << "Parent destroyed\n"; }
};class Node
{
public:std::weak_ptr<Parent> parent; // 使用 weak_ptr 避免循环引用~Node() { std::cout << "Node destroyed\n"; }
};int main()
{auto parent = std::make_shared<Parent>();auto node = std::make_shared<Node>();parent->child = node;node->parent = parent; // 不会增加引用计数// 离开作用域后对象都能正常销毁return 0;
}
- 智能指针特点比较
| 特性 | unique_ptr | shared_ptr | weak_ptr |
|---|---|---|---|
| 所有权 | 独占 | 共享 | 无 |
| 拷贝操作 | ❌ | ✅ | ✅ |
| 移动操作 | ✅ | ✅ | ✅ |
| 引用计数 | 无 | 有 | 观察计数 |
| 线程安全 | 否 | 控制块安全 | 同shared |
| 自定义删除器 | ✅ | ❌ | ❌ |
智能指针的原理
智能指针的原理主要依赖于C++的RAII(资源获取即初始化)机制,它通过封装指针来管理资源(如内存、文件句柄等)的生命周期。智能指针在构造时获取资源,在析构时释放资源,确保在程序结束时自动清理,从而避免了内存泄漏和悬空指针等问题。
- 智能指针的基本原理
智能指针是封装了裸指针的类,它管理着指向对象的生命周期。当智能指针超出作用域时,它会自动释放资源,防止忘记手动删除内存等问题。智能指针的实现通常依赖于以下几个核心概念:
- 构造函数:在创建智能指针时,智能指针会初始化并持有一个裸指针,指向一个动态分配的资源。
- 析构函数:当智能指针超出作用域时,析构函数会自动调用,释放资源(例如:delete)。
- 拷贝控制:智能指针需要对资源的所有权进行管理,避免多个指针共享同一资源时出现的问题。拷贝构造函数、赋值运算符和移动构造函数会控制资源的所有权转移。
- 智能指针的内存管理原理
智能指针通过管理内存的生命周期来避免内存泄漏。它们利用C++的构造和析构机制,当智能指针对象销毁时,自动调用析构函数,确保内存资源被正确释放。
- unique_ptr:独占资源,不允许其他指针共享资源的所有权。当unique_ptr析构时,自动删除它所持有的资源。
- shared_ptr:使用引用计数机制,当资源被多个shared_ptr共享时,资源在最后一个shared_ptr析构时才会被释放。
- weak_ptr:通过不增加引用计数来避免循环引用问题,它是一个观察者,不负责资源的释放。
内存泄漏
什么是内存泄漏,内存泄漏的危害
什么是内存泄漏:内存泄漏指因为疏忽或错误造成程序未能释放已经不再使⽤的内存,⼀般是忘记释放或者发⽣异常释放程序未能执⾏导致的。内存泄漏并不是指内存在物理上的消失,⽽是应⽤程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因⽽造成了内存的浪费。
内存泄漏的危害:普通程序运⾏⼀会就结束了出现内存泄漏问题也不⼤,进程正常结束,⻚表的映射关系解除,物理内存也可以释放。⻓期运⾏的程序出现内存泄漏,影响很⼤,如操作系统、后台服务、⻓时间运⾏的客⼾端等等,不断出现内存泄漏会导致可⽤内存不断变少,各种功能响应越来越慢,最终卡死。
int main(){// 申请⼀个1G未释放,这个程序多次运⾏也没啥危害 // 因为程序⻢上就结束,进程结束各种资源也就回收了char* ptr = new char[1024 * 1024 * 1024];cout << (void*)ptr << endl;return 0;}
如何检测内存泄漏
内存泄漏的本质
内存泄漏(Memory Leak)是指程序在动态分配内存后,失去了对该内存的控制权,导致无法释放,最终耗尽系统内存资源。
典型特征:
-
程序运行时间越长,内存占用持续增长
-
重复执行相同操作时内存消耗不断增加
-
最终可能导致程序崩溃或系统变慢
高级检测手段
| 工具名称 | 平台 | 特点 |
|---|---|---|
| Dr. Memory | 跨平台 | 比 Valgrind 更快 |
| Intel Inspector | Windows | 提供图形化界面 |
| LeakSanitizer | Linux | 与 ASan 集成 |
如何避免内存泄漏
-
⼯程前期良好的设计规范,养成良好的编码规范,申请的内存空间记着匹配的去释放。ps:这个理想状态。但是如果碰上异常时,就算注意释放了,还是可能会出问题。需要下⼀条智能指针来管理才有保证。
-
尽量使⽤智能指针来管理资源,如果⾃⼰场景⽐较特殊,采⽤RAII思想⾃⼰造个轮⼦管理。
-
定期使⽤内存泄漏⼯具检测,尤其是每次项⽬快上线前,不过有些⼯具不够靠谱,或者是收费。
-
总结⼀下:内存泄漏⾮常常⻅,解决⽅案分为两种:1、事前预防型。如智能指针等。2、事后查错型。如泄漏检测⼯具。
C++11和boost中智能指针的关系
- Boost库是为C++语⾔标准库提供扩展的⼀些C++程序库的总称,Boost社区建⽴的初衷之⼀就是为C++的标准化⼯作提供可供参考的实现,Boost社区的发起⼈Dawes本⼈就是C++标准委员会的成员之⼀。在Boost库的开发中,Boost社区也在这个⽅向上取得了丰硕的成果,C++11及之后的新语法和库有很多都是从Boost中来的。
- C++98中产⽣了第⼀个智能指针auto_ptr。
- C++boost给出了更实⽤的scoped_ptr/scoped_array和shared_ptr/shared_array和weak_ptr等
- C++TR1,引⼊了shared_ptr等,不过注意的是TR1并不是标准版。
- C++11,引⼊了unique_ptr和shared_ptr和weak_ptr。需要注意的是unique_ptr对应boost的scoped_ptr。并且这些智能指针的实现原理是参考boost中的实现的。
unique_ptr自己实现
#include <iostream>template <typename T>
class MyUniquePtr
{
private:T* ptr;public:// 构造函数explicit MyUniquePtr(T* p = nullptr) : ptr(p) {}// 析构函数,自动释放资源~MyUniquePtr() {delete ptr;}// 移动构造函数MyUniquePtr(MyUniquePtr&& other) noexcept : ptr(other.ptr) {other.ptr = nullptr; // 转移所有权}// 移动赋值运算符MyUniquePtr& operator=(MyUniquePtr&& other) noexcept {if (this != &other) {delete ptr; // 释放当前资源ptr = other.ptr;other.ptr = nullptr; // 转移所有权}return *this;}// 禁用拷贝构造函数和拷贝赋值运算符MyUniquePtr(const MyUniquePtr&) = delete;MyUniquePtr& operator=(const MyUniquePtr&) = delete;// 解引用操作符T& operator*() const { return *ptr; }// 指针操作符T* operator->() const { return ptr; }// 获取裸指针T* get() const { return ptr; }
};class MyClass
{
public:void show() {std::cout << "MyClass show method\n";}
};int main()
{// 创建 MyUniquePtr,并自动释放资源MyUniquePtr<MyClass> ptr1(new MyClass());ptr1->show();// 使用移动语义MyUniquePtr<MyClass> ptr2 = std::move(ptr1);// ptr1 现在为空,ptr2 拥有 MyClass 的所有权if (!ptr1.get()) {std::cout << "ptr1 is null\n";}ptr2->show();return 0;
}
shared_ptr
#include <iostream>template <typename T>
class MySharedPtr
{
private:T* ptr;int* ref_count;public:// 构造函数explicit MySharedPtr(T* p = nullptr) : ptr(p), ref_count(new int(1)) {}// 析构函数~MySharedPtr() {--(*ref_count);if (*ref_count == 0) {delete ptr;delete ref_count;}}// 拷贝构造函数MySharedPtr(const MySharedPtr& other) : ptr(other.ptr), ref_count(other.ref_count) {++(*ref_count);}// 拷贝赋值运算符MySharedPtr& operator=(const MySharedPtr& other) {if (this != &other) {// 先减少当前对象的引用计数--(*ref_count);if (*ref_count == 0) {delete ptr;delete ref_count;}// 赋新值ptr = other.ptr;ref_count = other.ref_count;++(*ref_count);}return *this;}// 解引用操作符T& operator*() const { return *ptr; }// 指针操作符T* operator->() const { return ptr; }// 获取裸指针T* get() const { return ptr; }// 获取引用计数int use_count() const { return *ref_count; }
};class MyClass
{
public:void show() {std::cout << "MyClass show method\n";}
};int main()
{// 创建 MySharedPtr,并自动管理引用计数MySharedPtr<MyClass> ptr1(new MyClass());std::cout << "Reference count: " << ptr1.use_count() << std::endl;{MySharedPtr<MyClass> ptr2 = ptr1; // ptr2 共享 ptr1 的所有权std::cout << "Reference count: " << ptr1.use_count() << std::endl;ptr2->show();}// ptr2 离开作用域后,ptr1 的引用计数减一std::cout << "Reference count after ptr2 goes out of scope: " << ptr1.use_count() << std::endl;return 0;
}
相关文章:
深入理解 C++11 智能指针:独占、共享与弱引用的完美管理
文章目录 std::unique_ptr(独占式智能指针)std::shared_ptr(共享式智能指针)std::weak_ptr(弱引用智能指针)示例展示:智能指针的原理内存泄漏**什么是内存泄漏,内存泄漏的危害****如…...
AI Agent开发大全第四课-提示语工程:从简单命令到AI对话的“魔法”公式
什么是提示语工程?一个让AI“听话”的秘密 如果你曾经尝试过用ChatGPT或者其他大语言模型完成任务,那么你一定遇到过这样的情况:明明你的问题是清晰的,但答案却离题万里;或者你认为自己提供的信息足够详尽,可结果还是不理想。问题出在哪?很多时候并不是因为AI不够聪明,…...
大模型架构记录 【综述-文字版】
名词解释: Prompt :提示词,是一个非常关键的概念,它指的是用户输入的文本或指令,用于引导语言模型生成相应的回答或执行特定任务。 Prompt Engineering:(提示工程) 是一种通过设计…...
WebSocket:开启实时通信的新篇章
在当今的互联网应用中,实时交互已经成为不可或缺的一部分。无论是实时的在线聊天、股票行情更新,还是多人在线游戏,都需要一种高效的双向通信机制。而这正是 WebSocket 的用武之地。 本文将带你深入了解 WebSocket,探索其工作原理…...
【论文笔记】Transformer
Transformer 2017 年,谷歌团队提出 Transformer 结构,Transformer 首先应用在自然语言处理领域中的机器翻译任务上,Transformer 结构完全构建于注意力机制,完全丢弃递归和卷积的结构,这使得 Transformer 结构效率更高…...
使用CSS3实现炫酷的3D翻转卡片效果
使用CSS3实现炫酷的3D翻转卡片效果 这里写目录标题 使用CSS3实现炫酷的3D翻转卡片效果项目介绍技术要点分析1. 3D空间设置2. 核心CSS属性3. 布局和定位 实现难点和解决方案1. 3D效果的流畅性2. 卡片内容布局3. 响应式设计 性能优化建议浏览器兼容性总结 项目介绍 在这个项目中…...
SpringSecurity——基于角色权限控制和资源权限控制
目录 基于角色权限控制 1.1 自定义 UserDetailsService 1.2 加载用户角色 1.3. 给角色配置能访问的资源(使用切面拦截,使用注解) 总结 资源权限控制 2.2. 需要有一个用户;(从数据库查询用户) 2.2 基…...
红宝书第十一讲:超易懂版「ES6类与继承」零基础教程:用现实例子+图解实现
红宝书第十一讲:超易懂版「ES6类与继承」零基础教程:用现实例子图解实现 资料取自《JavaScript高级程序设计(第5版)》。 查看总目录:红宝书学习大纲 一、ES6类的核心语法:把事物抽象成“模板” 想象你要设…...
通信基本概念
系列文章目录 文章目录 系列文章目录前言一、消息、信息和信号1.消息的定义2.信号的定义3.信息的定义4.消息、信息和信号的关系5.通信的目标 二、通信系统的组成模型1.一般通信系统模型2.各部分说明3.模拟通信系统模型4.数字通信系统模型4.数字通信的特点数字通信的优点数字通信…...
Python为Word文档添加书签并打包成exe
背景简述 由于一些工作场景,需要从多个Word文档中找到出现的关键词,并阅读关键词的上下文内容。文件可能几十个,手动操作太要命了。所以python尝试处理。 目录 背景简述思路第一步、功能实现结果验证 第二步、打包成exe2-1、基础准备2-2、打…...
ROS导航工具包Navigation
一,安装 Navigation工具包包含在 navigation 元功能包中。你可以通过以下命令安装: sudo apt-get install ros-noetic-navigation 如果你使用的是其他ROS版本(如Melodic),将 noetic 替换为对应的版本名称(…...
BigEvent项目后端学习笔记(二)文章分类模块 | 文章分类增删改查全流程解析(含优化)
📖 模块概述 文章分类模块包括 新增文章分类、文章分类列表、获取文章分类详情、更新文章分类、删除文章分类 功能。本篇对于原项目进行了代码优化,将原先写在 Controller 层的业务逻辑代码迁移至了 Service 层。 🛠️ 技术实现要点 分组校…...
资金管理策略思路
详细描述了完整交易策略的实现细节,主要包括输入参数、变量定义、趋势判断、入场与出场条件、止损与止盈设置等多个方面。 输入参数(Input): EntryFrL (.6):多头入场的前一日波动范围的倍数。 EntryFrS (.3)࿱…...
UI-TARS与Midscene.js自动化探索
结合 Midscene.js 和 UI-TARS 大模型 实现 UI 页面自动化的可实施方案,涵盖环境配置、核心流程、代码示例及优化建议: 一、环境配置与工具集成 安装 Midscene.js 方式一:通过 Chrome 插件快速安装(适用于浏览器自动化场景&#x…...
关于 URH(Universal Radio Hacker) 的详细介绍、安装指南、配置方法及使用说明
URH:开源无线电协议分析工具 一、URH简介 URH 是一款开源的 无线电协议分析工具,专注于解码、分析和逆向工程无线通信协议(如 Wi-Fi、蓝牙、RFID、LoRa、Zigbee 等)。它支持信号捕获、协议树构建、数据可视化及自定义脚本扩展&a…...
工业软件的破局与重构:从技术依赖到自主创新的未来路径
工业软件作为现代工业的“神经与大脑”,不仅是制造业数字化转型的核心工具,更是国家工业竞争力的战略制高点。近年来,中国工业软件市场在政策驱动与技术迭代中迅猛发展,但核心技术受制于人的困境仍待突破。如何实现从“跟跑”到“…...
C++ 介绍STL底层一些数据结构
c 标准模板库中,set和map的底层实现通常基于红黑树,然们都是平衡二叉搜索树(Balanceed Binary Serach Tree)的一种,这种结构保证了 插入,删除,查找的时间复杂度为O(log n)比普通二叉搜索树更高效。 set set<T>…...
CAJ转PDF:复杂的转换背后有哪些挑战?
CAJ转PDF:复杂的转换背后有哪些挑战? CAJ文件格式作为中国学术期刊的标准格式,广泛应用于学术文献的存储和分享,尤其是在中国知网(CNKI)中。然而,这种专有格式也带来了许多使用上的不便&#x…...
LeetCode Hot 100 - 子串 | 560.和为K的子数组、239.滑动窗口最大值、76.最小覆盖子串
560.和为K的子数组 前缀和哈希表 要查找的子数组为连续的,可以由两个前缀和计算得出,满足题目的条件为preSum[i] - preSum[j-1] k,所以我们可以用哈希表记录前缀和出现的次数,在遍历到位置 i 时计算出preSum[i] - k ,查看哈希表中是否有对…...
AI比人脑更强,因为被植入思维模型【18】万物系统思维模型
把事物看成链,看成网,看成生态。 定义 万物系统思维模型是一种将宇宙万物视为一个相互关联、相互作用的整体系统的思维方式。它强调从系统的角度去认识、分析和解决问题,认为系统中的各个要素之间存在着复杂的相互关系,这些关系不…...
常见中间件漏洞攻略-Tomcat篇
一、 CVE-2017-12615-Tomcat put方法任意文件写入漏洞 第一步:开启靶场 第二步:在首页抓取数据包,并发送到重放器 第三步:先上传尝试一个1.txt进行测试 第四步:上传后门程序 第五步:使用哥斯拉连接 二、后…...
小智物联网开发:为小智安装“机械臂“(其实就是加个舵机进行语音控制)
小智物联网开发:打造专属智能助手,开启智能生活新纪元 在物联网蓬勃发展的今天,小智物联网开发正引领着一股创新浪潮,为我们的生活和工作带来前所未有的便利与智能体验。今天,就让我们一起深入探索小智物联网开发的魅…...
【Dive Into Stable Diffusion v3.5】2:Stable Diffusion v3.5原理介绍
【Dive Into Stable Diffusion v3.5】系列博文: 第1篇:开源项目正式发布——深入探索SDv3.5模型全参/LoRA/RLHF训练第2篇:Stable Diffusion v3.5原理介绍 目录 1 前言1.1 扩散模型的原理1.2 损失函数1.3 加噪流程1.4 推理流程1.5 negative pr…...
线段树与扫描线 —— 详解算法思想及其C++实现
目录 一、线段树(Segment Tree) 基本概念 结构 操作 示例代码 二、扫描线(Sweep Line) 基本概念 应用场景 示例代码(矩形面积并集) 三、总结 一、线段树(Segment Tree) 基本…...
英伟达黄仁勋2025GTC演讲深度解析:液冷GPU、AI工厂、机器人AI…...
目录 一、技术产品与架构升级:从芯片到算力工厂1. 新一代GPU与计算架构2. AI工厂与算力操作系统 二、AI技术演进:从生成式到物理AI1. AI发展的三大阶段2. 推理算力需求爆炸式增长 三、生态合作与行业落地1. CUDA生态与开源工具2. 跨行业合作案例 四、未来…...
雷电模拟器启动94%卡住不动解决方案
安卓模拟器启动失败/启动加载卡0-29%/启动卡50%/启动卡94%的解决方法 首先看官方论坛常见问题来尝试解决: 安卓模拟器启动失败/启动加载卡0-29%/启动卡50%/启动卡94%的解决方法-雷电安卓模拟器-手游模拟器安卓版_android手机模拟器电脑版_雷电模拟器帮助中心 所有…...
02、聊天会话记忆ChatMemory
一、ChatMemory 由于手动维护和管理ChatMessages很麻烦,LangChain4j提供了ChatMemory抽象以及多个开箱即用的实现。 ChatMemory可以作为独立的低级组件来使用,也可以作为高级组件(AiService)的一部分使用。 ChatMemory作为Chat…...
vue3 ts 封装axios,配置axios前置拦截器,让所有axios请求携带token
vue3 ts 封装axios,配置axios前置拦截器,让所有axios请求携带token http.tsapp.tsvue文件 http.ts import axios from axios // 引入axios import router from /router import Qs from qs import { ElMessage } from element-plusconst { prefixBasePath } requir…...
嵌入式项目:利用心知天气获取天气数据实验方案
【实验目的】 1、利用心知天气服务器获取指定位置天气数据 2、将天气数据解析并可视化显示到OLED屏幕 【实验原理】 【实验步骤】 官网注册...
Ubuntu下用QEMU模拟运行OpenBMC
1、前言 在调试过程中,安装了很多依赖库,具体没有记录。关于kvm,也没理清具体有什么作用。本文仅记录,用QEMU成功的将OpenBMC跑起来的过程,做备忘,也供大家参考。 2、环境信息 VMware Workstation 15 Pro…...
