对于C++:内存管理的解析

C++ 的内存管理是语言最核心、最容易出错、也最能体现“现代 vs 传统”差异的部分。

下面从最实用的现代 C++ 视角(C++11/14/17/20/23)来完整梳理,帮助你建立清晰的思维框架。

一、C++ 内存管理的三大时代(你要知道自己站在哪个时代)

时代代表写法内存安全程度现代项目是否推荐备注
C 风格时代new / delete / malloc / free★☆☆☆☆几乎不推荐极易泄漏、双 delete、悬垂指针
半现代时代auto_ptr + 手动 new/delete★★☆☆☆已过时C++98/03 时代的过渡产物
现代 C++RAII + 智能指针 + 容器★★★★★强烈推荐C++11 之后的主流写法

2025 年真实项目里,99% 的新代码应该只出现在第三行

二、现代 C++ 内存管理的核心理念(背下来这 4 句话)

  1. 谁分配谁负责释放(所有权清晰)
  2. 资源获取即初始化(RAII) —— 构造时获取,析构时释放
  3. 尽量避免显式 delete(让编译器/标准库帮你管)
  4. 默认使用栈 > 智能指针 > 裸指针(从安全到危险排序)

三、现代 C++ 中最常用的 7 种内存管理方式(按使用频率排序)

优先级方式所有权语义典型场景是否推荐 new/delete
1局部变量(栈上)作用域结束自动销毁99% 的小对象、临时变量绝对不写 new
2std::unique_ptr<T>独占所有权需要动态生命周期,但只有一个拥有者推荐 make_unique
3std::shared_ptr<T>共享所有权(引用计数)需要多处共享、延迟销毁的场景推荐 make_shared
4std::vector<T> / std::string 等容器容器负责动态大小的序列、字符串基本不用 new[]
5std::weak_ptr<T>非拥有(弱引用)解决 shared_ptr 循环引用
6自定义 RAII 封装类资源(文件/锁/句柄)数据库连接、文件、互斥锁、socket 等
7裸指针(作为观察者)无所有权函数参数、回调、遍历(不负责释放)可以用,但慎用

四、现代写法 vs 传统写法的对比(强烈建议全部记住)

// 传统写法(千万不要再这样写新代码)voidbad_style(){ Widget* w =newWidget(args);// ... 可能抛异常、return、gotoif(some_condition){delete w;// 很容易漏return;}// ... 更多分支delete w;// 双 delete 风险}
// 现代写法(C++14/17 之后首选)#include<memory>voidgood_style(){auto w = std::make_unique<Widget>(args);// 推荐!// 或者 C++11 时代的写法(次选)// std::unique_ptr<Widget> w{new Widget(args)};// 异常安全、作用域结束自动 delete// 无需显式 delete}

共享所有权场景(最常见的工厂模式)

classWidget{/* ... */}; std::shared_ptr<Widget>createWidget(Params p){return std::make_shared<Widget>(p);// 一次分配控制块+对象,性能更好// return std::shared_ptr<Widget>(new Widget(p)); // 次选,效率稍低}

五、2024-2025 年最值得记住的“现代 C++ 内存管理口诀”

  1. 优先使用 make_unique / make_shared(异常安全 + 性能更好)
  2. 不要用 new 直接初始化智能指针(除非你有非常特殊理由)
  3. 函数参数优先用 T& / const T& / T&&,而不是 shared_ptr 传参(除非明确需要共享所有权)
  4. 回调/观察者模式用 weak_ptr + lock() 判断对象是否存活
  5. 永远不要在容器里放裸指针(除非你用自定义 deleter 或非常明确生命周期)
  6. 自定义资源用 RAII 封装类(文件、锁、句柄、GPU资源等)
  7. 尽量少用裸 new/delete(除非你在写:分配器、内存池、极致性能场景)

六、常见问题快速对照表

问题传统写法容易犯的错误现代正确做法
内存泄漏忘记 delete / 异常路径漏掉用 unique_ptr / shared_ptr / 容器
悬垂指针对象先销毁,指针还活着用 weak_ptr + lock() 检查
双重释放多次 delete智能指针自动管理
循环引用shared_ptr 互相指向一方改用 weak_ptr
性能浪费频繁 new/delete用内存池、arena allocator、小对象优化
传参膨胀到处传 shared_ptr优先用引用 / 原始指针(观察者语义)

七、快速自测(建议你默写答案)

  1. std::make_uniquenew 的主要区别是什么?为什么更推荐前者?
  2. 什么时候应该用 shared_ptr 而不是 unique_ptr
  3. 函数参数收到 shared_ptr<Widget>Widget* 分别代表什么语义?
  4. 如何用 weak_ptr 避免循环引用?
  5. 下面代码是否有问题?怎么改成现代写法?
std::vector<Widget*> widgets;for(int i=0; i<10; i++){ widgets.push_back(newWidget());}// ... 后面忘了释放

如果你对其中任何一块(比如自定义分配器、enable_shared_from_this、内存池、C++20/23 新工具、性能调优等)还想深入,都可以直接告诉我,我再给你展开更具体的例子和代码。

Read more

万物互联的起点:走进 Linux 网络的心脏,开启一场从零开始的底层探索之旅

万物互联的起点:走进 Linux 网络的心脏,开启一场从零开始的底层探索之旅

🔥海棠蚀omo:个人主页                 ❄️个人专栏:《初识数据结构》,《C++:从入门到实践》,《Linux:从零基础到实践》,《Linux网络:从不懂到不会》                 ✨追光的人,终会光芒万丈 博主简介: 目录 一.计算机网络背景 二.初识协议 三.协议分层 3.1软件分层的好处 3.2OSI七层模型 3.3TCP/IP五层(或四层)模型 四.再识协议 4.1为什么要有TCP/IP协议? 4.2TCP/IP协议与操作系统的关系 4.3究竟什么是协议? 五.网络传输基本流程 5.1局域网通信原理 5.2两台主机发送消息的过程 5.3跨网络传输 前言: 作为一名开发者,我们每天都在与网络打交道,

By Ne0inhk
Linux ELF格式与可执行程序加载全解析:从磁盘文件到运行进程

Linux ELF格式与可执行程序加载全解析:从磁盘文件到运行进程

🔥个人主页:Cx330🌸 ❄️个人专栏:《C语言》《LeetCode刷题集》《数据结构-初阶》《C++知识分享》 《优选算法指南-必刷经典100题》《Linux操作系统》:从入门到入魔 《Git深度解析》:版本管理实战全解 🌟心向往之行必能至 🎥Cx330🌸的简介: 目录 前言: 一、ELF文件:Linux二进制的标准载体 1.1 ELF文件的四大类型 1.2 ELF文件的双重视角:Section与Segment 1.3 ELF核心结构:从头部到加载指引 (1)ELF Header(文件头) (2)Program Header Table(程序头表) (3)Section Header Table(节头表) 二. ELF 的生命周期:从源码到运行

By Ne0inhk
Flutter 组件 ipaddr 适配鸿蒙 HarmonyOS 实战:高性能 IP 地址解析,构建子网掩码治理与网络边界安全架构

Flutter 组件 ipaddr 适配鸿蒙 HarmonyOS 实战:高性能 IP 地址解析,构建子网掩码治理与网络边界安全架构

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 ipaddr 适配鸿蒙 HarmonyOS 实战:高性能 IP 地址解析,构建子网掩码治理与网络边界安全架构 前言 在鸿蒙(OpenHarmony)生态迈向工业级物联网、涉及复杂内网穿透、防火墙规则动态配置及高性能路由器网关开发的背景下,如何精准地处理 IPv4 与 IPv6 的双栈解析,已成为决定网络应用“链路安全性”与“协议合规性”的关键工程要素。在鸿蒙设备这类强调分布式安全域与网络边界动态防御的环境下,如果应用依然依赖简单的字符串分割进行 IP 校验,由于由于输入格式的模糊性(如不规范的 IPv6 缩写),极易由于由于“解析逻辑漏洞”导致非法的流量注入或子网越权。 我们需要一种能够支持 CIDR 表示法、具备子网包含性判定(Inclusion Check)且符合 RFC

By Ne0inhk
Linux 动态链接与动态库加载深度解析

Linux 动态链接与动态库加载深度解析

🔥草莓熊Lotso:个人主页 ❄️个人专栏: 《C++知识分享》《Linux 入门到实践:零基础也能懂》 ✨生活是默默的坚持,毅力是永久的享受! 🎬 博主简介: 文章目录 * 前言: * 一. 进程如何感知并加载动态库 * 1.1 进程对动态库的 “可见性” * 1.2 多进程共享动态库的实现 * 二. 动态链接的核心工作原理 * 2.1 程序运行前的动态链接准备 * 2.2 动态库的地址无关性:PIC 编译 * 2.3 运行时的地址重定位:从符号到实际地址 * 三. GOT/PLT:动态链接的核心实现机制 * 3.1 全局偏移量表(GOT) * 3.2 过程链接表(PLT):延迟绑定优化 * 3.

By Ne0inhk