首页 > 后端开发 > C++ > 正文

C++中new一个数组为什么要用delete[]来释放

P粉602998670
发布: 2025-08-31 08:21:01
原创
332人浏览过
C++中new和new[]的核心区别在于:new用于单个对象的分配与构造,delete用于其释放;new[]用于对象数组的分配,会调用多个构造函数并存储元素数量,必须用delete[]释放以正确调用每个对象的析构函数并释放内存。若用delete释放new[]分配的数组,将导致未定义行为,可能引发内存泄漏、堆损坏或程序崩溃。现代C++推荐使用std::vector或std::unique_ptr<T[]>、std::shared_ptr<T[]>等智能指针管理动态数组,遵循RAII原则,避免手动内存管理风险。

c++中new一个数组为什么要用delete[]来释放

在C++中,当你使用

new[]
登录后复制
来动态分配一个对象数组时,你必须使用
delete[]
登录后复制
来释放它。这并非一种建议,而是语言规范强制要求,其根本在于确保内存的正确回收以及数组中每个对象的析构函数都能被调用。如果使用
delete
登录后复制
(不带方括号)来释放一个数组,将导致未定义行为,通常表现为内存泄漏(因为只有第一个或没有对象的析构函数被调用)或堆损坏。

解决方案

当我们用

new T[N]
登录后复制
分配一个T类型对象的数组时,编译器不仅仅是简单地分配了一大块原始内存。它还会在这块内存上依次调用N个T类型对象的构造函数,并将这N个对象的地址妥善管理起来。更关键的是,为了让
delete[]
登录后复制
知道需要析构多少个对象,通常会在分配的内存块头部(或者其他隐蔽的地方)存储数组的元素数量N。

当执行

delete[] ptr
登录后复制
时,C++运行时会从
ptr
登录后复制
指向的内存区域获取之前存储的元素数量N。然后,它会逆序依次调用这N个T类型对象的析构函数,确保每个对象在被销毁前都能执行其清理工作。最后,整个原始内存块才会被释放回系统。

而如果你错误地使用了

delete ptr
登录后复制
来释放一个
new[]
登录后复制
分配的数组,运行时只会尝试调用
ptr
登录后复制
所指向的第一个对象的析构函数(如果T是非POD类型),或者根本不调用任何析构函数(如果T是POD类型,比如
int
登录后复制
)。更糟糕的是,它会尝试以处理单个对象的方式来释放内存,这可能导致内存管理系统内部状态混乱,因为它无法正确识别和处理数组头部的元数据。这种不匹配的操作,正是未定义行为的根源,轻则内存泄漏,重则程序崩溃。

立即学习C++免费学习笔记(深入)”;

C++中
new
登录后复制
new[]
登录后复制
操作符的核心区别是什么?

从表面上看,

new
登录后复制
new[]
登录后复制
都用于动态内存分配,但它们之间存在着本质的区别,这直接关系到如何正确地构造和销毁对象。理解这些差异,是掌握C++内存管理的关键一步。

首先,

new T
登录后复制
用于分配单个T类型对象的内存,并调用其构造函数。它返回一个指向这个新创建的T类型对象的指针(
T*
登录后复制
)。当这个对象不再需要时,我们使用
delete ptr
登录后复制
来释放内存,并调用其析构函数。这是一种一对一的关系:一个
new
登录后复制
对应一个
delete
登录后复制

new T[N]
登录后复制
则用于分配一个包含N个T类型对象的数组。它会分配足够容纳N个T类型对象的内存,并且会为数组中的每一个元素依次调用T的构造函数。它同样返回一个
T*
登录后复制
类型的指针,指向数组的第一个元素。但这里的关键在于,运行时系统需要某种机制来记住这个数组的大小N,以便在后续销毁时知道要调用多少次析构函数。因此,
new[]
登录后复制
在内部通常会做一些额外的工作,比如在实际分配的内存块头部存储这个N值。与此对应,我们必须使用
delete[] ptr
登录后复制
来释放这个数组。
delete[]
登录后复制
会根据之前存储的大小信息,逆序遍历数组中的每个元素,并依次调用它们的析构函数,最后才释放整个内存块。

所以,核心区别在于:

new
登录后复制
处理单个对象的生命周期管理,而
new[]
登录后复制
处理一系列相同类型对象的生命周期管理。这种差异体现在它们对构造函数和析构函数的调用次数,以及内存块内部元数据的处理方式上。这种设计上的对称性,要求我们必须使用匹配的分配和释放操作符。

如果我用
delete
登录后复制
释放了
new[]
登录后复制
创建的数组,会发生什么?

这绝对是一个危险的操作,结果是未定义行为(Undefined Behavior, UB)。在C++中,一旦触发未定义行为,就意味着程序可能做任何事情:它可能看似正常运行一段时间,然后突然崩溃;它可能产生错误的结果;它甚至可能在不同的运行环境或编译器版本下表现出完全不同的行为。你无法预测,也无法依赖其结果。

具体来说,当你用

delete ptr
登录后复制
来释放一个通过
new T[N]
登录后复制
分配的数组时,通常会发生以下几种情况:

  1. 内存泄漏(Memory Leak):这是最常见的结果。如果T是一个具有非平凡析构函数的类(即它的析构函数有实际的清理工作,比如释放内部资源),那么

    delete ptr
    登录后复制
    只会尝试调用
    ptr
    登录后复制
    指向的第一个元素的析构函数,而数组中剩余N-1个元素的析构函数将永远不会被调用。这意味着这些对象内部持有的资源(例如,它们自己内部
    new
    登录后复制
    出来的内存、文件句柄、网络连接等)将无法得到释放,从而导致内存泄漏和其他资源泄漏。即使T是像
    int
    登录后复制
    这样的基本类型,虽然没有析构函数需要调用,但内存管理系统仍然会因为不匹配的释放操作而产生问题。

  2. 堆损坏(Heap Corruption):这是更严重的问题。

    new[]
    登录后复制
    在分配内存时,通常会在实际数据块之前存储一些元数据,比如数组的大小。
    delete[]
    登录后复制
    知道如何解析这些元数据来正确地释放内存。而
    delete
    登录后复制
    则期望处理一个没有这些额外元数据的内存块。当你用
    delete
    登录后复制
    来释放一个带有
    new[]
    登录后复制
    元数据的内存块时,
    delete
    登录后复制
    可能会错误地解释这些元数据,或者尝试释放一个它不认为属于自己的内存区域,这会导致堆管理器的内部结构被破坏。一旦堆被损坏,后续的内存分配和释放操作都可能失败,甚至导致整个程序崩溃。

  3. 程序崩溃(Program Crash):堆损坏通常会导致程序在后续的内存操作中立即或稍后崩溃,表现为段错误(segmentation fault)或其他访问冲突。这种崩溃可能发生在与

    delete
    登录后复制
    操作直接相关的代码行,也可能发生在程序中完全不相关的其他地方,这使得调试变得异常困难。

    阿里云-虚拟数字人
    阿里云-虚拟数字人

    阿里云-虚拟数字人是什么? ...

    阿里云-虚拟数字人 2
    查看详情 阿里云-虚拟数字人

总之,用

delete
登录后复制
释放
new[]
登录后复制
创建的数组是一个严重的编程错误,应当不惜一切代价避免。它破坏了C++内存管理的内在契约,导致不可预测且难以诊断的问题。

在C++中管理动态数组,除了
new[]/delete[]
登录后复制
,还有哪些更安全、推荐的方式?

在现代C++编程中,我们强烈建议尽可能避免直接使用原始的

new[]
登录后复制
delete[]
登录后复制
。C++标准库提供了更安全、更易于管理动态数组的替代方案,它们遵循RAII(Resource Acquisition Is Initialization)原则,能自动处理内存的分配和释放,大大降低了内存泄漏和错误使用的风险。

  1. std::vector<T>
    登录后复制
    :这是C++中最常用、最推荐的动态数组解决方案。
    std::vector
    登录后复制
    是一个模板类,它提供了动态大小的数组功能。当你向
    vector
    登录后复制
    中添加元素时,它会自动管理底层内存的重新分配;当你
    vector
    登录后复制
    超出作用域时,它会自动释放所有内存并调用所有元素的析构函数。它提供了丰富的成员函数,如
    push_back
    登录后复制
    pop_back
    登录后复制
    at
    登录后复制
    (带边界检查)、
    size
    登录后复制
    empty
    登录后复制
    等,功能强大且使用方便。对于绝大多数需要动态数组的场景,
    std::vector
    登录后复制
    都是首选。

    • 优点:自动内存管理,无需手动

      delete[]
      登录后复制
      ;自动调用构造函数和析构函数;支持随机访问;效率高;提供边界检查(
      at()
      登录后复制
      );可动态增长或收缩。

    • 示例

      #include <vector>
      #include <string>
      
      std::vector<std::string> names; // 创建一个空的字符串向量
      names.push_back("Alice");
      names.push_back("Bob");
      // ... 当names超出作用域时,所有字符串对象都会被正确析构,内存也会被释放。
      登录后复制
  2. 智能指针管理动态数组

    • std::unique_ptr<T[]>
      登录后复制
      :当你需要一个拥有独占所有权的动态数组,并且不希望它被共享时,
      std::unique_ptr<T[]>
      登录后复制
      是一个非常好的选择。它确保了当
      unique_ptr
      登录后复制
      超出作用域时,底层的数组内存会被自动释放,并且数组中所有元素的析构函数都会被调用。它专门为数组设计了特化版本,因此不需要自定义删除器。

      • 优点:独占所有权,避免悬空指针;自动调用

        delete[]
        登录后复制
        ;轻量级。

      • 示例

        #include <memory>
        #include <iostream>
        
        // 创建一个包含5个int的数组
        std::unique_ptr<int[]> arr = std::make_unique<int[]>(5);
        for (int i = 0; i < 5; ++i) {
            arr[i] = i * 10;
        }
        // ... 当arr超出作用域时,delete[]会被自动调用。
        登录后复制
    • std::shared_ptr<T[]>
      登录后复制
      :如果你的动态数组需要在多个地方共享所有权,那么
      std::shared_ptr<T[]>
      登录后复制
      是合适的选择。它通过引用计数来管理内存,只有当所有
      shared_ptr
      登录后复制
      实例都销毁后,底层的数组内存才会被释放。同样,它也提供了针对数组的特化版本。

      • 优点:共享所有权;自动调用

        delete[]
        登录后复制
        ;适用于多所有者场景。

      • 示例

        #include <memory>
        #include <iostream>
        
        std::shared_ptr<double[]> shared_arr(new double[10]); // 需要使用裸new[]作为参数
        // 或者更现代的方式,但make_shared不支持数组版本,所以通常是这样
        // std::shared_ptr<double[]> shared_arr = std::make_shared<double[]>(10); // C++20开始支持
        
        // ... shared_arr可以被复制,只有当最后一个shared_ptr销毁时,内存才会被释放。
        登录后复制

虽然

new[]/delete[]
登录后复制
是C++语言的一部分,理解其工作原理至关重要,但在实际项目中,优先选择
std::vector
登录后复制
和智能指针来管理动态数组,能够显著提高代码的安全性、可读性和维护性,避免手动内存管理带来的诸多陷阱。

以上就是C++中new一个数组为什么要用delete[]来释放的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号