当前位置: 首页 > news >正文

C++ new 创建数组的内在原理详解

C++ new 创建数组的内在原理详解

核心流程

当使用 new Type[n] 创建数组时,编译器会执行以下步骤:

  1. 内存分配

    • 调用 operator new[] 函数(底层通常为 malloc
    • 计算总内存大小:n * sizeof(Type) + 额外开销
    • 额外开销:存储数组元素数量(通常 4/8 字节,编译器相关)
  2. 构造对象

    • 按顺序调用每个元素的构造函数(从索引 0 到 n-1)
    • 对 POD 类型(基本类型/简单结构)可能跳过构造步骤
  3. 返回指针

    • 返回指向第一个元素的指针(非完整内存块起始地址)
int* arr = new int[5]; 
// 实际内存布局:
// [数组大小][int0][int1][int2][int3][int4]
// ↑          ↑
// 分配地址    返回指针

内存布局(编译器实现示例)
内存偏移内容大小
0数组大小记录 (n=5)4/8 字节
4/8arr[0]sizeof(int)
4/8+20arr[4]sizeof(int)

析构过程 (delete[])
  1. 获取数组大小

    • 通过返回指针向前偏移读取存储的数组大小
  2. 逆序析构

    • 按倒序调用每个元素的析构函数(从索引 n-1 到 0)
  3. 释放内存

    • 调用 operator delete[](底层通常为 free
delete[] arr;  // 正确释放方式

关键原理剖析

  1. 内存开销机制

    class MyClass {
    public:MyClass() { std::cout << "Constructed\n"; }~MyClass() { std::cout << "Destructed\n"; }
    };MyClass* arr = new MyClass[3];
    /* 输出:ConstructedConstructedConstructed
    */
    
    • 内存开销:32 位系统通常额外 +4 字节,64 位系统 +8 字节
    • 验证sizeof(MyClass)*3 + sizeof(size_t) < 实际分配内存
  2. 析构顺序重要性

    delete[] arr; 
    /* 输出(逆序析构):DestructedDestructedDestructed
    */
    
  3. new[]/delete[] 严格配对
    错误示例:

    int* arr = new int[10];
    delete arr;  // 未定义行为!应使用 delete[]
    
    • 可能导致:内存泄漏(未完全释放)/ 堆损坏
    • 根本原因delete 不会查找数组大小记录

编译器实现差异

编译器调试模式额外开销释放模式优化
MSVC内存保护字节(32字节)最小开销(仅数组大小)
GCC/Clang无额外保护字节同 MSVC

与 malloc/free 的本质区别

操作new[]/delete[]malloc/free
内存分配调用构造函数原始内存分配
内存释放调用析构函数仅释放内存
类型安全类型感知无类型信息
大小处理自动存储数组大小需手动记录大小
失败处理抛出 std::bad_alloc返回 NULL

经典错误案例解析

案例 1:错误释放方式

MyClass* arr = new MyClass[100];
free(arr);  // 错误!

后果:

  1. 未调用任何析构函数 → 资源泄漏
  2. 未使用分配器匹配的释放函数 → 堆结构损坏

案例 2:跨模块分配释放

// DLL模块A
__declspec(dllexport) int* createArray() {return new int[100];
}// EXE主程序
void useArray() {int* arr = createArray();delete[] arr;  // 可能崩溃!
}

原因:不同堆管理器分配/释放内存(DLL 和 EXE 使用不同 CRT)


最佳实践

  1. 优先使用标准容器

    std::vector<int> arr(100);  // 自动管理内存
    
  2. 明确所有权语义

    // C++11 起推荐使用智能指针
    auto arr = std::make_unique<int[]>(100); 
    
  3. POD类型优化

    int* arr = new (std::nothrow) int[1'000'000]; // 不抛异常版本
    if(!arr) { /* 处理分配失败 */ }
    
  4. 自定义内存管理

    // 重载类专属 operator new[]
    class MyClass {
    public:static void* operator new[](size_t size) {std::cout << "Allocating " << size << " bytes\n";return ::operator new[](size);}
    };
    

性能特征

操作时间复杂度说明
new Type[n]O(n)需调用 n 次构造函数
delete[]O(n)需调用 n 次析构函数
内存分配O(1) 摊销取决于堆分配器实现
http://www.dtcms.com/a/292511.html

相关文章:

  • linux 环境服务发生文件句柄泄漏导致服务不可用
  • ELF 文件操作手册
  • python学习-读取csv文件
  • 如何验证分类模型输出概率P值的“好坏”:评估与校准示例
  • GitHub 上的开源项目 ticktick(滴答清单)
  • recvmsg函数的用法
  • 算法学习--滑动窗口
  • 学习python中离线安装pip及下载package的方法
  • C语言:函数基础
  • day059-zabbix自定义监控与自动发现
  • Node.js:Web模块、Express框架
  • es6中的symbol基础知识
  • 在Android开发中,如何获取到手机设备的PIN码?
  • 如何安装CMake较新的版本
  • Apache Ignite 长事务终止机制
  • 精密全波整流电路(一)
  • torchvision.transforms 与 MONAI 数据增强的异同
  • Cloud 与 VPS 的区别:如何选择最适合你的服务器解决方案?
  • stream流入门
  • 【打怪升级 - 01】保姆级机器视觉入门指南:硬件选型 + CUDA/cuDNN/Miniconda/PyTorch 安装全流程(附版本匹配秘籍)
  • vmware 克隆虚拟机,报错:克隆时出错:指定不存在的设备。然后电脑卡死,只能强制关机再开机。
  • FastDFS 6.11.0 单机环境搭建与测试(附 Nginx 集成)+ docker构建+k8s启动文件
  • 用org.apache.pdfbox 转换 PDF 到 图片格式
  • KafkaMQ 日志采集最佳实践
  • Python 正则表达式:入门到实战
  • 日常随笔-React摘要
  • 【ROS/DDS】FastDDS :编写FastDDS程序实现与ROS2 通讯(四)
  • 深入浅出理解 TCP 与 UDP:网络传输协议的核心差异与应用
  • 平台端用户管理功能设计全解:从分类管控到审核闭环
  • 基于springboot的疫苗发布和接种预约系统(论文+开题报告)