C++ 中动态内存分配(new/delete) 的核心基础知识

一、为什么需要 new/delete?

c++的内存分配主要是栈内存和堆内存,两者的核心区别决定了new/delete的必要性:

内存区域分配 / 销毁方式大小限制生命周期示例
栈内存编译器自动分配 / 销毁小(通常几 MB)随作用域结束销毁int num = 10;、Student s;
堆内存手动用 new 分配 /delete 销毁大(受物理内存限制)手动释放前一直存在int* p = new int;Student* s = new Student();

栈内存的核心痛点:

  • 栈内存大小有限,创建大数组 / 大对象时会栈溢出;
  • 栈对象生命周期固定,无法在函数结束后保留数据;
  • 动态数据结构(链表、树)需要灵活的内存分配,栈内存无法满足。

new/delete的核心作用:手动管理堆内存,解决栈内存的局限性。

二、new的核心用法(分配堆内存)

new是 C++ 的动态内存分配运算符,作用是向操作系统申请指定大小的堆内存,返回指向该内存的指针。

1.基本语法(四种核心场景)

场景语法示例说明
分配单个基本类型变量int* p = new int;申请 4 字节堆内存,返回int*指针;内存值为随机数(未初始化)
分配单个变量并初始化int* p = new int(10);申请 4 字节堆内存,初始化为 10;支持所有基本类型(char* c = new char('a');
分配数组int* arr = new int[5];申请 20 字节堆内存(5 个 int),返回指向第一个元素的指针;元素值随机
分配数组并初始化(C++11+)int* arr = new int[5]{1,2,3,4,5};申请 20 字节堆内存,元素初始化为指定值;未指定的元素默认初始化为 0(比如new int[5]{1,2} → [1,2,0,0,0])

2. 分配自定义类型(类、结构体)

#include <iostream>
#include <string>
using namespace std;

class Person {
public:
    string name;
    int age;
    // 构造函数
    Person() : name("未知"), age(0) {
        cout << "无参构造调用" << endl;
    }
    Person(string n, int a) : name(n), age(a) {
        cout << "带参构造调用" << endl;
    }
};

int main() {
    // 1. 分配单个对象(调用构造函数)
    Person* p1 = new Person(); // 无参构造
    Person* p2 = new Person("张三", 20); // 带参构造

    // 2. 分配对象数组(调用无参构造)
    Person* pArr = new Person[2]; // 2个对象,各调用一次无参构造

    return 0;
}

三、delete 的核心用法(释放堆内存)

delete是 C++ 的内存释放运算符,作用是将堆内存归还给操作系统,必须和 new 配对使用。

1. 基本语法(必须和new严格配对)

new 的用法delete 的对应用法说明
new 类型(单个变量)delete 指针;释放单个变量的堆内存;释放后指针变为野指针
new 类型[长度](数组)delete[] 指针;释放数组的堆内存;必须加 [ ],否则内存泄漏(自定义类型还会导致析构函数不调用)

2. 完整示例(new/delete 配对)

#include <iostream>
using namespace std;

int main() {
    // 1. 单个变量:new → delete
    int* p = new int(10);
    cout << *p << endl; // 10
    delete p; // 释放内存
    p = nullptr; // 置空,避免野指针

    // 2. 数组:new[] → delete[]
    int* arr = new int[5]{1,2,3,4,5};
    for (int i = 0; i < 5; i++) {
        cout << arr[i] << " "; // 1 2 3 4 5
    }
    delete[] arr; // 释放数组(必须加[])
    arr = nullptr;

    // 3. 自定义类型:new → delete
    class Person {
    public:
        ~Person() { cout << "析构函数调用" << endl; }
    };
    Person* pObj = new Person();
    delete pObj; // 释放时自动调用析构函数
    pObj = nullptr;

    return 0;
}

delete 之后为什么要置空指针:本质是为了避免野指针导致的未定义行为——delete 仅释放堆内存,但不会改变指针本身的值,指针仍指向已释放的内存地址,此时的指针就是 “野指针”,后续误操作会引发程序崩溃、数据错乱等严重问题。

四、new/delete 的核心规则(面试必记)

1. 严格配对原则(最核心)

  • newdelete:一对一,少删内存泄漏,多删程序崩溃;
  • new[]delete[]:必须加[],否则:
    • 基本类型数组:大概率内存泄漏(操作系统可能未检测);
    • 自定义类型数组:析构函数仅调用第一个对象,其余对象内存泄漏。

2. 野指针规避(面试高频)

  • 释放内存后,必须将指针置空(p = nullptr);
  • 空指针释放合法:delete nullptr;(编译器无操作,无需额外判断)。

3. 内存泄漏的核心场景(避坑)

内存泄漏:堆内存未释放且指针丢失,导致内存永久占用,常见场景:

// 场景1:分配后未释放
void func1() {
    int* p = new int(10);
    // 无delete → 函数结束后指针p销毁,堆内存泄漏
}

// 场景2:指针重赋值,丢失原内存地址
void func2() {
    int* p = new int(10);
    p = new int(20); // 原10的内存地址丢失,泄漏
    delete p; // 仅释放20的内存
}

// 场景3:数组释放漏写[]
void func3() {
    int* arr = new int[5];
    delete arr; // 错误:应delete[] arr → 内存泄漏
}

4. new 的失败处理(进阶)

  • 早期 C++:new 分配失败返回NULL
  • 现代 C++:new 分配失败抛出bad_alloc异常(可捕获):
#include <iostream>
#include <new> // 包含bad_alloc
using namespace std;

int main() {
    try {
        // 申请超大内存,触发失败
        int* p = new int[1000000000000];
        delete[] p;
    } catch (bad_alloc& e) {
        cout << "内存分配失败:" << e.what() << endl;
    }
    return 0;
}

五、new/delete vs malloc/free(面试高频对比)

C++ 兼容 C 的malloc/free,但 new/delete 更适合 C++,核心区别:

维度new/deletemalloc/free
本质C++ 运算符C 函数
构造 / 析构自动调用(自定义类型)仅分配 / 释放内存,不调用
语法new int(10)(直接初始化)malloc(4)(需手动计算字节数)+ memset初始化
返回值直接返回对应类型指针(无需强制转换)返回void*,需强制转换(int* p = (int*)malloc(4);
失败处理抛异常(默认)或返回 NULL返回 NULL
数组处理new[]/delete[]需手动计算总字节数(malloc(5*4)

六、总结

  • 核心作用:new 手动分配堆内存(解决栈内存局限),delete 手动释放堆内存(避免泄漏);
  • 配对规则newdeletenew[]delete[],缺一不可;
  • 避坑要点
    • 释放内存后指针置空(nullptr),避免野指针;
    • 数组释放必须加[],自定义类型会影响析构函数调用;
    • 避免指针重赋值导致内存泄漏;
  • 面试对比:new/delete 是 C++ 运算符,自动调用构造 / 析构;malloc/free 是 C 函数,仅处理内存。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值