[C++]new和delect的动态内存分配

一、什么是动态内存分配?

在C和C++中,程序运行时可以根据需要分配内存空间。这就叫做动态内存分配 。它与静态内存分配不同,静态内存分配是编译时就决定好了内存的大小,而动态内存分配则是程序在运行时根据情况来分配内存。

比如你在编写程序时,不知道需要多少个整数,或者数组的大小在运行时才知道,这时候就可以使用动态内存分配。

二、为什么要用动态内存?

想象一下,程序在运行时可能要处理很多数据,数据的大小不确定。**如果在编译时就决定数组的大小,那就有可能内存浪费或者不够用。**动态内存分配的优势就是你可以在运行时根据需要调整内存的大小,节省资源。

动态内存分配的目的是在运行时按需分配内存空间。C++ 提供了两个操作符来管理动态内存:

  • new:用于在堆上分配内存。
  • delete :用于释放 new 分配的内存。

三、newnew[]:动态分配内存

C++提供了两个关键字来做动态内存分配:newnew[]

  • new 用来分配一个对象的内存,指向这个对象。
  • new[] 用来分配一段连续的内存空间,指向数组的第一个元素。

1.new 语法:

cpp 复制代码
int* p = new int; // 分配一个 int 大小的内存
  • new :在堆上分配了一个 int 类型的内存,大小是 sizeof(int),并返回这块内存的地址。
  • p:是一个指针,它存储的是动态分配内存的地址。

2.new[ ]语法:

cpp 复制代码
int* arr = new int[5]; // 分配一个包含 5 个 int 元素的数组
  • new int[5] :这里 new 操作符动态地在堆上分配了一块足够容纳 5 个 int 类型数据的内存区域。每个 int 通常占用 4 字节(视平台而定),所以总共分配了 5 * 4 字节的内存。

  • 返回值new int[5] 返回的是这块内存的首地址 ,即第一个 int 元素的地址。

  • 这里的操作依然是在堆上分配内存,但与单个变量不同,分配的是一个数组 ,包含 5 个 int 类型的元素,arr 是指向这个数组的指针。通过 arr[0]arr[1] ...来访问数组的元素。

四、 deletedelete[]:释放动态内存

动态分配的内存使用完之后,必须手动释放,否则会造成内存泄漏 。C++提供了 deletedelete[] 来释放内存。

  • delete 用来释放单个对象的内存。
  • delete[] 用来释放数组的内存。

1.delete 语法:

cpp 复制代码
delete p; // 释放通过 new 分配的内存

delete 释放的是 单个对象 的内存。

2.delete[ ] 语法:

cpp 复制代码
delete[] arr; // 释放通过 new[] 分配的数组内存

delete[] 用来释放通过 new[] 分配的数组 的内存。如果用 new[] 分配了一个数组,就必须用 delete[] 来释放这个数组。

五、内存泄漏和如何避免

内存泄漏是指忘记释放 分配的内存,导致内存不断积累,最终耗尽系统内存。内存泄漏的一个常见情况是分配了内存后,没有调用 deletedelete[] 释放它。

1.示例:

cpp 复制代码
int* p = new int;  // 分配了内存
// 忘记调用 delete 释放内存

2.如何避免:

  • 每次使用 newnew[] 分配内存后,都要在不再使用内存时,使用 deletedelete[] 释放它。

六、nullptr:空指针

在C++中,nullptr 用来表示指针不指向任何有效的内存。初始化指针 时,如果不指向任何地址,可以将它设置为 nullptr,这样可以避免指针指向垃圾数据。

示例:

cpp 复制代码
int* p = nullptr;  // 指针 p 不指向任何地方

当指针不再需要时,可以将它设置为 nullptr,并且要尽量避免让指针指向已经释放的内存,这样就避免了悬空指针的问题。

七、内存分配失败时的处理

newnew[] 在分配内存失败时会抛出bad_alloc 异常。如果你不想让程序因内存分配失败而崩溃,可以使用 new(nothrow),它不会抛出异常,而是返回 nullptr

示例:

cpp 复制代码
int* p = new(std::nothrow) int[1000];  // 如果内存不足,p 为 nullptr
if (!p) {
    std::cout << "Memory allocation failed!" << std::endl;
}

八、newdelete 与类构造函数/析构函数

当使用 new 分配内存时,如果是分配类对象的内存,会调用类的构造函数。如果使用 delete 释放内存,会调用类的析构函数。

cpp 复制代码
class MyClass {
public:
    MyClass() { std::cout << "MyClass 构造函数" << std::endl; }
    ~MyClass() { std::cout << "MyClass 析构函数" << std::endl; }
};

MyClass* ptr = new MyClass;  // 调用构造函数
delete ptr;  // 调用析构函数并释放内存

九、综合代码示例:

1.代码:

cpp 复制代码
#include <iostream>
#include <new>  // 用于 nothrow 和 bad_alloc
using namespace std;

// 定义一个简单的类,演示构造函数和析构函数的调用
class MyClass {
public:
    MyClass() {
        cout << "MyClass 构造函数" << endl;
    }

    ~MyClass() {
        cout << "MyClass 析构函数" << endl;
    }
};

int main() {
    // 1. 使用 new 分配内存
    int* p = new(nothrow) int;  // 在堆上分配一个 int 类型的内存
    if (!p) {
        cout << "内存分配失败!" << endl;
        return 1;  // 如果内存分配失败,退出程序
    }
    *p = 10;  // 给 p 指向的内存赋值
    cout << "p 指向的值: " << *p << endl;

    // 2. 使用 new[] 分配内存
    int* arr = new(nothrow) int[5];  // 在堆上分配一个包含 5 个元素的 int 数组
    if (!arr) {
        cout << "数组内存分配失败!" << endl;
        delete p;  // 释放之前分配的单个内存
        return 1;
    }

    // 给数组赋值并打印
    for (int i = 0; i < 5; ++i) {
        arr[i] = (i + 1) * 10;  // 给数组元素赋值
        cout << "arr[" << i << "] = " << arr[i] << endl;
    }

    // 3. 使用 delete 释放单个对象内存
    delete p;  // 释放之前分配的单个 int 类型的内存
    p = nullptr;  // 将指针设为 nullptr,避免悬空指针

    // 4. 使用 delete[] 释放数组内存
    delete[] arr;  // 释放之前分配的数组内存
    arr = nullptr;  // 将数组指针设为 nullptr

    // 5. 使用 new 来分配类对象的内存
    MyClass* myObj = new(nothrow) MyClass();  // 分配 MyClass 类型的对象
    if (!myObj) {
        cout << "类对象内存分配失败!" << endl;
        return 1;
    }

    // 6. 使用 delete 释放类对象的内存
    delete myObj;  // 会调用 MyClass 的析构函数

    return 0;
}

2.输出:

cpp 复制代码
MyClass 构造函数
p 指向的值: 10
arr[0] = 10
arr[1] = 20
arr[2] = 30
arr[3] = 40
arr[4] = 50
MyClass 析构函数

3.代码讲解:

1.newnew[]

  • new std::nothrow int;:分配一个 int 类型的单个对象,并在内存分配失败时不会抛出异常,而是返回 nullptr
  • new std::nothrow int[5];:分配一个包含 5 个 int 元素的数组,并且同样使用 std::nothrow 来避免抛出异常。

2.内存分配失败的处理

  • 使用 if (!p)if (!arr) 来检查内存分配是否成功。如果失败,就输出错误信息并退出程序。

3.释放内存

  • delete p; 释放由 new 分配的单个对象内存。
  • delete[] arr; 释放由 new[] 分配的数组内存。

4.使用 nullptr

  • 在释放内存后,将指针设置为 nullptr,避免指针悬空的问题。

5.构造函数与析构函数

  • new MyClass():当 new 分配一个类对象的内存时,会调用该类的构造函数。
  • delete myObj;:当调用 delete 时,会自动调用类的析构函数来释放资源。

十、总结:

  1. new:动态分配单个对象的内存。
  2. new[]:动态分配一个数组的内存。
  3. delete :释放由 new 分配的内存。
  4. delete[] :释放由 new[] 分配的数组内存。
  5. nullptr:表示指针不指向任何有效内存。
  6. 每次动态分配内存后,都要记得释放,以避免内存泄漏。

十一、小贴士:

  • 不要忘记释放动态分配的内存。
  • 使用 new[] 分配数组时,一定要用 delete[] 来释放。
  • 使用 nullptr 可以帮助管理指针,避免错误使用。
相关推荐
疯狂的沙粒15 分钟前
JavaScript 单例模式的创建与应用
开发语言·前端·javascript·vue.js
余额不足1213817 分钟前
C语言基础六:循环结构及面试上机题
c语言·开发语言
m0_748256561 小时前
Rust环境安装配置
开发语言·后端·rust
程序猿阿伟1 小时前
《C++巧铸随机森林:开启智能决策新境界》
开发语言·c++·随机森林
阿客不是客1 小时前
深入计算机语言之C++:STL之list的模拟实现
数据结构·c++·stl
假意诗人1 小时前
【NextJS】Arco Design与Next.js快速上手
开发语言·javascript·arco design
凡人的AI工具箱1 小时前
40分钟学 Go 语言高并发教程目录
开发语言·后端·微服务·性能优化·golang
pzx_0011 小时前
【论文阅读】相似误差订正方法在风电短期风速预报中的应用研究
开发语言·论文阅读·python·算法·leetcode·sklearn
每天写点bug1 小时前
【golang】匿名内部协程,值传递与参数传递
开发语言·后端·golang
抽风侠2 小时前
qt实现窗口的动态切换
开发语言·qt