C++之内存分配new与delete使用方式

 更新时间:2025年05月22日 10:03:00   作者:zzh_zao  
这篇文章主要介绍了C++之内存分配new与delete使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

C++ new 和 delete

在C++中,newdelete 是用于动态内存分配和释放的运算符。

它们允许程序在运行时管理堆内存,是实现动态数据结构(如链表、树)和灵活资源管理的基础。

1. 基本语法

new 运算符

  • 分配单个对象
Type* ptr = new Type;         // 默认初始化(内置类型值未定义)
Type* ptr = new Type(value);  // 直接初始化
Type* ptr = new Type{value};  // 列表初始化(C++11起)
  • 分配数组
Type* arr = new Type[size];   // 分配包含size个元素的数组
  • 值初始化
Type* ptr = new Type();       // 值初始化为0或默认构造函数

delete 运算符

  • 释放单个对象
delete ptr;  // 调用析构函数(如有)并释放内存
  • 释放数组
delete[] arr;  // 释放数组内存,需使用[]告知编译器释放多个对象

2. 工作原理

new 的执行步骤

  • 内存分配:调用 operator new(或 operator new[] 用于数组)分配原始内存。
  • 构造函数调用:在分配的内存上调用对象的构造函数(若为类类型)。

delete 的执行步骤

  • 析构函数调用:对对象调用析构函数(若为类类型)。
  • 内存释放:调用 operator delete(或 operator delete[])释放内存。

3. 深入理解 new 和 delete

原始内存操作

operator newoperator delete

底层函数,可被重载以自定义内存分配行为。

void* operator new(size_t size);      // 分配单个对象
void* operator new[](size_t size);    // 分配数组
void operator delete(void* ptr);      // 释放单个对象
void operator delete[](void* ptr);    // 释放数组

示例

void* raw_memory = operator new(sizeof(int));  // 分配原始内存
int* ptr = static_cast<int*>(raw_memory);      // 转换为int指针
*ptr = 42;                                      // 手动赋值
operator delete(ptr);                           // 释放内存(不调用析构函数)

定位 new(Placement New)

在已分配的内存上构造对象:

void* buffer = operator new(sizeof(MyClass));  // 分配原始内存
MyClass* obj = new (buffer) MyClass();         // 在buffer上构造对象
obj->~MyClass();                               // 显式调用析构函数
operator delete(buffer);                       // 释放内存

4. 数组的动态分配

分配与释放数组

int* arr = new int[10];       // 分配10个int的数组
delete[] arr;                 // 释放数组

// 多维数组
int** matrix = new int*[5];   // 分配5行
for (int i = 0; i < 5; ++i) {
    matrix[i] = new int[10];  // 每行10列
}
// 释放
for (int i = 0; i < 5; ++i) {
    delete[] matrix[i];
}
delete[] matrix;

数组初始化

int* arr = new int[5]{1, 2, 3, 4, 5};  // C++11起支持列表初始化

在对数组进行动态分配时,会自动在之前多分配一块空间用来储存所要开辟的空间个数,以便将来用delete释放空间。

5.new和delete的实现原理

5.1 内置类型

如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:

new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。

5.2 自定义类型

new的原理

  • 调用operator new函数申请空间
  • 在申请的空间上执行构造函数,完成对象的构造

delete的原理

  • 在空间上执行析构函数,完成对象中资源的清理工作
  • 调用operator delete函数释放对象的空间

new T[N]的原理

  • 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请
  • 在申请的空间上执行N次构造函数

delete[]的原理

  • 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
  • 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间

6. 常见问题与注意事项

内存泄漏

原因:忘记调用 delete 释放 new 分配的内存。

示例

void leak() {
    int* ptr = new int;  // 分配内存
    // 未释放内存就返回
}  // 内存泄漏!

悬空指针

原因:释放内存后仍保留指向该内存的指针。

示例

int* ptr = new int;
delete ptr;
*ptr = 10;  // 悬空指针解引用,未定义行为

重复释放

原因:多次释放同一块内存。

示例

int* ptr = new int;
delete ptr;
delete ptr;  // 重复释放,未定义行为

混用 delete 和 delete[]

错误示例

int* arr = new int[10];
delete arr;  // 错误!应使用delete[]

对于内置类型,程序会崩溃;而对于自定义类型,程序不会崩溃。

7. 自定义内存管理

重载全局 operator new 和 operator delete

void* operator new(size_t size) {
    void* p = malloc(size);
    // 可添加内存分配日志、性能监控等
    return p;
}

void operator delete(void* p) noexcept {
    free(p);
    // 可添加内存释放日志等
}

类特定的内存管理

class MyClass {
public:
    static void* operator new(size_t size) {
        // 自定义分配逻辑
        return ::operator new(size);
    }
    
    static void operator delete(void* p) noexcept {
        // 自定义释放逻辑
        ::operator delete(p);
    }
};

8. 与C语言内存管理的对比

特性C++ new/deleteC语言 malloc/free
类型安全自动推导类型,无需强制转换需要显式转换(如 (int*)malloc())
构造/析构函数自动调用构造函数和析构函数不调用构造/析构函数
初始化支持直接初始化和列表初始化仅分配内存,不初始化值
数组语法直接使用 new Type[size]需要计算总大小(如 malloc(size*sizeof(Type)))
智能指针支持与标准库智能指针无缝配合需手动封装为智能指针

malloc/free和new/delete的共同点是:都是从堆上申请空间,并且需要用户手动释放。

不同的地方是:

  • malloc和free是函数,new和delete是操作符
  • malloc申请的空间不会初始化,new可以初始化
  • malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可, 如果是多个对象,[]中指定对象个数即可
  • malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
  • malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
  • 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理释放

总结

new/delete 是C++动态内存分配的核心机制,适用于:

  • 需要在运行时确定对象生命周期。
  • 实现动态数据结构(如链表、树)。
  • 优先使用智能指针:现代C++中,std::unique_ptrstd::shared_ptr 能有效避免内存泄漏,提高代码安全性。
  • 谨慎手动管理内存:必须确保 newdelete 配对使用,避免悬空指针和重复释放。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 使用C/C++读取matlab中.mat格式数据的操作

    使用C/C++读取matlab中.mat格式数据的操作

    这篇文章给大家介绍了使用C/C++读取matlab中.mat格式数据的操作,文中通过图文结合的方式介绍的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2023-12-12
  • 深入c++中临时对象的析构时机的详解

    深入c++中临时对象的析构时机的详解

    本篇文章对c++中临时对象的析构时机进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • Qt QtCreator添加自定义注释的实现方法

    Qt QtCreator添加自定义注释的实现方法

    在写代码的时候我们为了规范化,一般会加文件注释、类注释和函数注释,本文主要介绍了Qt QtCreator添加自定义注释的实现方法,具有一定的参考价值,感兴趣的可以了解一下
    2023-11-11
  • C++高精度算法的使用场景详解

    C++高精度算法的使用场景详解

    在我们进行计算的过程中,经常会遇到几十位,甚至几百位的数字的计算问题,也有可能会遇到小数点后几十位,几百位的情况,而我们面对这样的情况下,long long  和 double 的数据范围显然是不够使用的了。因此这时,我们就需要引入一个新的算法,叫做高精度算法
    2022-09-09
  • C++ opencv将图片动漫化介绍

    C++ opencv将图片动漫化介绍

    大家好,本篇文章主要讲的是C++ opencv将图片动漫化介绍,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下
    2022-01-01
  • Qt可视化大屏布局的实现

    Qt可视化大屏布局的实现

    数据可视化大屏在项目中的使用很常见,本文主要介绍了Qt可视化大屏布局的实现,具有一定的参考价值,感兴趣的可以了解一下
    2024-02-02
  • CMake自动管理C/C++项目的实现

    CMake自动管理C/C++项目的实现

    CMake是一个强大的构建系统,用于跨平台管理C/C++项目的编译过程,本文主要介绍了CMake自动管理C/C++项目的实现,具有一定的参考价值,感兴趣的可以了解一下
    2025-02-02
  • 新版本Qt Creator安装配置的实现步骤

    新版本Qt Creator安装配置的实现步骤

    本文详细介绍了如何在Windows11环境下,使用Qt5.14.2和VS2017安装并配置最新版本的QtCreator10.0.2,文中通过图文示例介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧
    2025-07-07
  • C/C++程序开发中实现信息隐藏的三种类型

    C/C++程序开发中实现信息隐藏的三种类型

    这篇文章主要介绍了C/C++程序开发中实现信息隐藏的三种类型的相关资料,需要的朋友可以参考下
    2016-02-02
  • 一文详解C++中打开文件的多种方式及相关流类

    一文详解C++中打开文件的多种方式及相关流类

    在 C++ 中,打开文件可以通过多种流类实现,每种方式都有其特定的用途和特点,这篇文章将为大家详细介绍一下它们的具体使用,感兴趣的小伙伴可以了解下
    2025-07-07

最新评论