16、动态内存管理

厨子大约 3 分钟C++C++基础编程程序厨

内存管理

内存管理有几个相关概念我们需要掌握:

  • 堆****内存:由程序员手动分配和释放的内存区域,生命周期由程序员控制。
  • 栈****内存:由编译器自动分配和释放的内存区域,生命周期与作用域绑定。
  • 内存****泄漏:程序未释放不再使用的堆内存,导致内存占用不断增加。
  • 悬空指针:指针指向的内存已被释放,但指针仍又被使用。

内存管理的原则:

  • 谁分配,谁释放:确保每个动态分配的内存都有对应的释放操作。
  • 避免重复释放:同一块内存只能释放一次。
  • 避免悬空指针:释放内存后,将指针置为nullptr

示例代码:

#include <iostream>

int main() {
    int* ptr = new int(42); // 分配堆内存
    std::cout << "Value: " << *ptr << std::endl; // 输出:42

    delete ptr; // 释放堆内存
    ptr = nullptr; // 避免悬空指针

    if (ptr) {
        std::cout << "Pointer is not null." << std::endl;
    } else {
        std::cout << "Pointer is null." << std::endl; // 输出:Pointer is null.
    }

    return 0;
}

C++中的动态内存管理是指在程序运行时,根据需要从堆(heap)中分配和释放内存。与栈内存不同,堆内存的生命周期由程序员手动控制,因此需要更加谨慎地管理。

newdelete的基本用法

  1. **new**运算符:用于在堆上分配内存,并返回指向该内存的指针。
  2. **delete**运算符:用于释放由 new 分配的内存。

代码示例:

#include <iostream>
using namespace std;

int main() {
    // 动态分配一个 int 类型的内存
    int* p = new int;
    *p = 10;
    cout << *p << endl;

    // 释放内存
    delete p;

    return 0;
}

new[]delete[]

  1. new[]运算符:用于在堆上分配数组内存。
  2. delete[]运算符:用于释放由 new[]分配的数组内存。

代码示例:

#include <iostream>
using namespace std;

int main() {
    // 动态分配一个包含 5 个 int 元素的数组
    int* arr = new int[5];
    for (int i = 0; i < 5; ++i) {
        arr[i] = i + 1;
    }

    // 释放数组内存
    delete[] arr;

    return 0;
}

new[]delete[]一定要配对使用。

为什么要配对使用 newdeletenew[]delete[]

  • 内存泄漏: 如果只分配内存而不释放,会导致内存泄漏,最终耗尽系统内存。
  • 未定义行为: 使用 delete 释放 new[] 分配的内存,或者使用 delete[] 释放 new 分配的内存,都会导致未定义行为。

placement new

placement new是个进阶知识点了,它允许程序员在已分配的内存上构造对象,而不会分配新的内存,只是使用指定的内存地址。

代码示例:

#include <iostream>
using namespace std;

struct A {
    A(int a): a_(a) {}

    void print() {
        std::cout << a_ << std::endl;
    }
    int a_;
};

int main() {
    // 预先分配内存
    char* buffer = new char[sizeof(int)];

    // 在 buffer 上构造 A 对象
    A* p = new (buffer) A(10);
    p->print();

    // 显式调用析构函数
    p->~A();

    // 释放内存
    delete[] buffer;

    return 0;
}

newmalloc的区别

特性newmalloc
语言C++ 运算符C 库函数
返回值返回具体类型指针返回 void*
失败处理抛出 std::bad_alloc 异常返回 NULL
内存大小自动计算需要手动计算
构造函数调用构造函数不调用构造函数
析构函数调用析构函数不调用析构函数
重载可以重载不能重载

代码示例:

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

int main() {
    // 使用 new 分配内存
    int* p1 = new int(10);
    cout << *p1 << endl;
    delete p1;

    // 使用 malloc 分配内存
    int* p2 = (int*)malloc(sizeof(int));
    *p2 = 20;
    cout << *p2 << endl;
    free((void*)p2);

    return 0;
}

练习题

  1. 编写一个程序,动态分配一个包含 10 个 double 类型元素的数组,并初始化数组元素为 1.0 到 10.0,最后释放数组内存。
  2. 编写一个程序,使用 placement new 在一个预先分配的 char 数组上构造一个自定义class对象,并调用自定义class的相关函数。