C++ new 操作的背后都经历了什么?
[TOC](C new 操作的背后都经历了什么)!-- 顶部锚点 --br/table width100%trtd width50%div idtop aligncenterfont faceSTCAIYUN colorred size4emThe Begin点点关注收藏不迷路em/font/divbrdiv aligncenter stylemargin: 20px 0;a href#bottom styledisplay: inline-block; padding: 12px 35px; background: #0a0a0a; border: 2px solid #00ff88; border-radius: 8px; text-decoration: none; box-shadow: 0 0 15px rgba(0,255,136,0.5); transition: all 0.3s;font faceSTCAIYUN color#08979D size4 stylefont-weight: bold; letter-spacing: 2px;⬇ ⬇ 底部 ⬇ ⬇/font/a/div/td/tr/table1. 引言从一条语句到内存的旅程在 C 中我们经常写这样的代码int* p new int(42); MyClass* obj new MyClass();这条看似简单的new语句背后却隐藏着一条漫长而复杂的链路内存分配、类型转换、构造函数调用、异常处理…… 它远不止是malloc加一层包装那么简单。本文将深入剖析 C 中new操作的完整生命周期从编译器视角到操作系统底层带你一探new背后的全貌。---2. new 操作的本质分层一个完整的new表达式 (new expression) 可以拆解为三个层次| 层次 | 概念 | 职责 ||------|------|------|| 上层 |new 表达式(new expression) | 程序员写的new T由编译器负责翻译 || 中层 |operator new函数 | 负责原始内存的分配可重载 || 下层 |底层内存分配器(allocator) | 如malloc、HeapAlloc或自定义分配器 |它们的关系如下new T(args...) -- 调用 operator new 分配原始内存 -- 在内存上构造对象 (placement new) -- 返回指针---3. 第一步new 表达式的编译展开当我们写下new MyClass(10)时编译器会将其翻译为类似以下的伪代码// 伪代码展示编译器行为 void* raw_mem operator new(sizeof(MyClass)); // 1. 分配原始内存 if (raw_mem nullptr) { // 如果 operator new 抛异常则不会执行构造 } MyClass* ptr nullptr; try { ptr new (raw_mem) MyClass(10); // 2. placement new 调用构造函数 } catch (...) { operator delete(raw_mem); // 3. 构造失败则释放内存 throw; // 4. 重新抛出异常 } return ptr;编译器至少做了四件事调用operator new获取原始内存。在原始内存上调用构造函数 (通过 placement new)。如果构造函数抛出异常自动调用operator delete释放已分配的内存。返回构造好的对象指针。---4. 第二步operator new 函数operator new是 C 标准库中定义的全局函数负责分配原始内存不构造对象。它的典型声明是void* operator new(std::size_t size); // 普通版本 void* operator new(std::size_t size, std::nothrow_t) noexcept; // nothrow 版本4.1 默认实现标准库的默认operator new内部通常调用malloc并在失败时抛出std::bad_alloc异常。// glibc 简化示意 void* operator new(std::size_t size) { if (size 0) size 1; // C 标准要求即使 size0 也返回合法指针 void* ptr malloc(size); if (!ptr) { throw std::bad_alloc(); // 失败抛异常 } return ptr; }4.2 可重载性operator new可以被全局重载也可以在类内部重载用于实现自定义内存管理如内存池、对象池。class MyClass { public: static void* operator new(std::size_t size) { std::cout Custom new for MyClass, size size std::endl; return ::operator new(size); // 仍可回退到全局 } static void operator delete(void* ptr) { ::operator delete(ptr); } };4.3 带额外参数的 placement new标准库还提供了带额外参数的 placement new 版本最常见的是new (void*)用于在已分配的内存上构造对象。void* buffer malloc(sizeof(MyClass)); MyClass* obj new (buffer) MyClass(); // 在 buffer 位置构造对象 // 必须手动调用析构函数 obj-~MyClass(); free(buffer);你也可以自定义自己的 placement new 版本例如new (arena, 0)这种。---5. 第三步内存分配器底层operator new默认调用malloc但malloc本身也不是直接向操作系统申请内存的。现代 C/C 运行库的内存分配器 (如 glibc 的 ptmalloc2、tcmalloc、jemalloc) 通常采用以下策略| 内存大小 | 分配策略 ||----------|----------|| 小对象 ( 64KB) | 从线程本地缓存或空闲链表中复用避免频繁系统调用 || 大对象 ( 64KB) | 直接通过mmap向 OS 申请匿名内存页 |这种分层设计极大提升了小对象频繁分配/释放的性能。最终当分配器向操作系统申请内存时会通过系统调用Linuxbrk/sbrk(堆扩展) 或mmap(匿名映射)WindowsHeapAlloc/VirtualAlloc---6. 第四步对象构造 (Constructor)内存分配完成后接下来是调用构造函数。这一步通过placement new实现编译器会在目标内存地址上直接调用构造函数。// 等价于编译器生成的代码 new (raw_mem) MyClass(10);注意构造函数可以抛出异常。如果构造过程中抛出异常operator new已经分配的内存需要被释放否则会造成内存泄漏。这就是为什么前面编译器生成的伪代码中有try-catch结构保证异常安全。---7. 第五步数组 new (new[])当我们写new MyClass[10]时过程略有不同调用operator new[]分配内存默认也是调用operator new。对于有非平凡构造函数的类编译器会在数组头部额外存储数组长度以便在delete[]时知道需要调用多少次析构函数。依次对每个元素调用默认构造函数或初始化器。// 编译器对 new[] 的展开示意 void* raw operator new[](sizeof(MyClass) * count extra_bytes); // 在头部存储 count size_t* header static_castsize_t*(raw); *header count; MyClass* data static_castMyClass*(raw extra_bytes); for (size_t i 0; i count; i) { new (data i) MyClass(); // 依次构造 }---8. 内存释放与析构delete 的对称过程delete操作正好是new的逆过程调用对象的析构函数 (如果是数组则按逆序调用所有元素的析构函数)。调用operator delete(或operator delete[]) 释放原始内存。operator delete默认调用free归还内存给分配器。// delete ptr 的伪代码 if (ptr ! nullptr) { ptr-~MyClass(); // 1. 调用析构 operator delete(ptr); // 2. 释放内存 }重要delete和free绝对不能混用用new分配的内存必须用delete用malloc分配的内存必须用free。---9. 完整流程图否是是否new T(args)计算所需字节数 (可能包含对齐/数组头部)调用 operator new(size_t)operator new 调用底层分配器 (如 malloc)底层分配器: 线程缓存 / 空闲链表 / mmap/brk从 OS 获取虚拟内存 (物理页按需映射)返回原始内存指针 raw内存分配成功?抛出 std::bad_alloc结束 (异常)在 raw 上执行 placement new调用构造函数 T::T(args)构造是否抛出异常?operator delete(raw) 释放内存重新抛出异常返回类型化的指针 T*结束 (正常)---10. 特殊情况与进阶话题10.1 nothrow new使用std::nothrow版本分配失败时返回nullptr而不是抛出异常int* p new (std::nothrow) int[1000]; if (p nullptr) { // 处理分配失败 }10.2 自定义 new 处理函数 (new_handler)可以通过std::set_new_handler设置一个全局回调函数当operator new分配失败时在抛出异常前会反复调用此函数给你机会释放内存或重试。void my_handler() { std::cerr Out of memory! Trying to release...\n; // 尝试释放一些缓存 } std::set_new_handler(my_handler);10.3 内存对齐对于需要特定对齐的类型 (如alignas(64)的类)operator new需要返回对齐的地址。C17 引入了operator new的对齐版本void* operator new(std::size_t size, std::align_val_t alignment);10.4 new 与构造函数中的 virtual 函数在构造函数中调用虚函数不会发生多态因为派生类尚未构造完成。这虽然是语法允许的但属于设计上需要避免的反模式。---11. 总结new 的完整链路| 步骤 | 操作 | 执行者 | 可自定义? ||------|------|--------|-----------|| 1 | 解析 new 表达式计算大小 | 编译器 | ❌ || 2 | 调用operator new| 编译器插入 | ✅ (可重载) || 3 | 底层内存分配 (malloc/mmap) | 标准库/OS | ✅ (可替换分配器) || 4 | 内存对齐和填充 | 分配器 | ❌ || 5 | 调用构造函数 (placement new) | 编译器 | ❌ || 6 | 异常时自动回收内存 | 编译器 | ❌ || 7 | 返回指针 | - | ❌ |snew的每一次调用都是编译器、运行库、操作系统三者紧密协作的结果。理解这条链路不仅有助于写出更高效的内存管理代码也能在遇到内存泄漏、异常安全、性能瓶颈等问题时做到心中有数、定位准确。---!-- 底部锚点 --br/table width100%trtd width50%div aligncenterfont faceSTCAIYUN colorred size4emThe End点点关注收藏不迷路em/font/divbrdiv idbottom aligncenter stylemargin: 20px 0;a href#top styledisplay: inline-block; padding: 12px 35px; background: #0a0a0a; border: 2px solid #00ff88; border-radius: 8px; text-decoration: none; box-shadow: 0 0 15px rgba(0,255,136,0.5); transition: all 0.3s;font faceSTCAIYUN color#08979D size4 stylefont-weight: bold; letter-spacing: 2px;⬆ ⬆ 顶部 ⬆ ⬆/font/a/div/td/tr/table

相关新闻

Unity相机与交互系统实战:飞行、路径、晃动相机及触摸控制

Unity相机与交互系统实战:飞行、路径、晃动相机及触摸控制

1. 项目概述:从单一交互到沉浸式体验的构建 在Unity项目开发中,尤其是涉及到3D展示、产品演示、数字孪生或游戏场景时,如何让用户与虚拟世界进行自然、流畅且富有表现力的交互,是提升体验的关键。一个静态的、只能平移缩放旋转的相…

2026/7/27 10:04:38 阅读更多 →
大语言模型文本长度限制:技术原理与工程优化实践

大语言模型文本长度限制:技术原理与工程优化实践

这次我们来看一个关于大语言模型(LLMs)与文本长度限制的技术话题——"LLMs and the Curse of the Word Count"。这个标题直指大语言模型在处理长文本时面临的核心挑战:随着输入文本长度的增加,模型性能如何变化&#xf…

2026/7/27 10:04:38 阅读更多 →
深入浅出华为S7700交换机接口管理:从MTU玄学到配置实战,一篇搞定

深入浅出华为S7700交换机接口管理:从MTU玄学到配置实战,一篇搞定

[TOC](深入浅出华为S7700交换机接口管理&#xff1a;从MTU玄学到配置实战&#xff0c;一篇搞定)<!-- 顶部锚点 --> <br/> <table width"100%"><tr><td width"50%"><div id"top" align"center">&l…

2026/7/27 10:04:38 阅读更多 →

最新新闻

LM86温度传感器:SMBus通信、数字滤波与PCB布局实战指南

LM86温度传感器:SMBus通信、数字滤波与PCB布局实战指南

1. LM86温度传感器&#xff1a;从芯片手册到实战应用的深度解析在服务器主板、工控设备或者高性能嵌入式系统的研发过程中&#xff0c;温度监控从来都不是一个“锦上添花”的功能&#xff0c;而是系统稳定性的生命线。我经历过不止一次因为一颗小小的温度传感器读数漂移&#x…

2026/7/27 10:21:45 阅读更多 →
mandodb查询优化指南:从Range查询到Series匹配的实战技巧

mandodb查询优化指南:从Range查询到Series匹配的实战技巧

mandodb查询优化指南&#xff1a;从Range查询到Series匹配的实战技巧 【免费下载链接】mandodb &#x1f914; A minimize Time Series Database, written from scratch as a learning project. 从零开始实现一个 TSDB 项目地址: https://gitcode.com/gh_mirrors/ma/mandodb …

2026/7/27 10:21:45 阅读更多 →
掌握@rc-component/progress高级特性:分段进度与渐变色彩实现技巧

掌握@rc-component/progress高级特性:分段进度与渐变色彩实现技巧

掌握rc-component/progress高级特性&#xff1a;分段进度与渐变色彩实现技巧 【免费下载链接】progress React Progress Bar 项目地址: https://gitcode.com/gh_mirrors/prog/progress rc-component/progress是一款功能强大的React进度条组件&#xff0c;不仅支持基础的…

2026/7/27 10:21:45 阅读更多 →
LeafTidy 轻羽归档|零风险免费电脑文件整理工具,告别传统整理软件的各种痛点

LeafTidy 轻羽归档|零风险免费电脑文件整理工具,告别传统整理软件的各种痛点

LeafTidy 轻羽归档&#xff5c;零风险免费电脑文件整理工具&#xff0c;告别传统整理软件的各种痛点 摘要&#xff1a;电脑文件杂乱、相册无序、重复文件占用大量硬盘空间。传统整理工具体验卡顿、操作繁琐、还要付费订阅&#xff0c;联网还有隐私隐患。LeafTidy 搭载自研本地文…

2026/7/27 10:21:45 阅读更多 →
终极指南:5分钟掌握Zotero插件市场,打造你的专属文献管理工具箱

终极指南:5分钟掌握Zotero插件市场,打造你的专属文献管理工具箱

终极指南&#xff1a;5分钟掌握Zotero插件市场&#xff0c;打造你的专属文献管理工具箱 【免费下载链接】zotero-addons Zotero Add-on Market | Zotero插件市场 | Browsing and installing plugins within Zotero 项目地址: https://gitcode.com/gh_mirrors/zo/zotero-addon…

2026/7/27 10:21:45 阅读更多 →
RTX 4060训练SolidWorks操作AI的实践与优化

RTX 4060训练SolidWorks操作AI的实践与优化

1. 项目概述&#xff1a;用RTX 4060训练SolidWorks操作AI的可行性分析作为一名在工业设计自动化领域工作多年的工程师&#xff0c;我最近完成了一个有趣的实验&#xff1a;用一张RTX 4060显卡训练能够操作SolidWorks的AI助手。这个项目的核心挑战在于&#xff0c;我们需要让AI像…

2026/7/27 10:20:45 阅读更多 →

日新闻

【JAVA毕设源码分享】基于SpringBoot的社区智能垃圾管理系统的设计与实现(程序+文档+代码讲解+一条龙定制)

【JAVA毕设源码分享】基于SpringBoot的社区智能垃圾管理系统的设计与实现(程序+文档+代码讲解+一条龙定制)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

2026/7/27 0:00:54 阅读更多 →
SPI实战指南:从时钟模式到寄存器配置,解决嵌入式通信难题

SPI实战指南:从时钟模式到寄存器配置,解决嵌入式通信难题

1. 项目概述&#xff1a;从寄存器手册到实战指南 如果你手头有一份类似德州仪器&#xff08;TI&#xff09;TMS320x240xA系列DSP的SPI模块技术手册&#xff0c;看着里面密密麻麻的寄存器位定义、时序图和公式&#xff0c;是不是感觉头大&#xff1f;这份资料虽然权威&#xff0…

2026/7/27 0:00:54 阅读更多 →
【JAVA毕设源码分享】基于springboot的水果购物管理系统的设计与实现(程序+文档+代码讲解+一条龙定制)

【JAVA毕设源码分享】基于springboot的水果购物管理系统的设计与实现(程序+文档+代码讲解+一条龙定制)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

2026/7/27 0:00:54 阅读更多 →

周新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档&#xff0c;可以直接使用&#xff01;系统支持图片、视频、摄像头等多种方式检测裂缝&#xff0c;功能强大实用。 1数据集6000张 8各类别

2026/7/27 4:33:59 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像&#xff01; pubg绝地求生目标检测数据集 1分类&#xff1a;e_body&#xff0c;14905个标签&#xff0c;txt格式 共计14244张图&#xff0c;99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/7/27 6:31:56 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别&#xff1a; allies enemy tag图片总量&#xff1a;7247张训练集&#xff1a;5139张验证集&#xff1a;1425张测试集&#xff1a;683张标注状态&#xff1a;全部已标注&#xff0c;即拿即用数据格式&#xff1a;支持YOLO格式及其他格式&#…

2026/7/27 4:01:12 阅读更多 →

月新闻