内存分配与释放

在 C++ 中,内存管理是一个重要的概念。程序员需要了解如何在堆上分配和释放内存,以确保程序的正确性和效率。本小节将介绍 C++ 中的内存分配与释放的基本知识。

1. 栈与堆

C++ 中的内存分为栈(stack)和堆(heap)两种主要区域:

  • :用于存储局部变量和函数调用信息。栈上的内存由编译器自动管理,超出变量作用域时自动释放。
  • :用于动态分配内存,程序员需要手动管理。堆上的内存在显式释放之前会一直存在。

2. 栈内存分配

栈内存分配用于局部变量,编译器自动管理其生命周期。

#include <iostream>

void stackAllocation() {
    int x = 10; // 栈上分配内存
    std::cout << "Value of x: " << x << std::endl;
} // x 在函数结束时自动释放

int main() {
    stackAllocation();
    return 0;
}

3. 堆内存分配

堆内存分配用于动态分配内存,程序员需要手动管理其生命周期。C++ 提供了 newdelete 运算符来管理堆内存。

3.1 使用 new 分配内存

new 运算符用于在堆上分配内存,并返回指向分配内存的指针。

#include <iostream>

int main() {
    int* p = new int(10); // 在堆上分配内存
    std::cout << "Value of *p: " << *p << std::endl;
    delete p; // 释放堆上的内存
    return 0;
}

3.2 使用 delete 释放内存

delete 运算符用于释放由 new 分配的内存。如果忘记释放内存,会导致内存泄漏。

#include <iostream>

int main() {
    int* p = new int(10);
    std::cout << "Value of *p: " << *p << std::endl;
    delete p; // 释放堆上的内存
    // p = nullptr; // 释放后将指针设为 nullptr 以避免悬空指针
    return 0;
}

3.3 数组的动态分配和释放

使用 newdelete 可以动态分配和释放数组的内存。

#include <iostream>

int main() {
    int* arr = new int[5]; // 动态分配数组内存
    for (int i = 0; i < 5; ++i) {
        arr[i] = i * 10;
        std::cout << "arr[" << i << "] = " << arr[i] << std::endl;
    }
    delete[] arr; // 释放数组内存
    return 0;
}

4. 智能指针

C++11 引入了智能指针来简化内存管理,避免手动管理内存带来的错误。常用的智能指针有 std::unique_ptrstd::shared_ptrstd::weak_ptr

4.1 std::unique_ptr

std::unique_ptr 是一种独占所有权的智能指针,不能共享所有权。

#include <iostream>
#include <memory>

int main() {
    std::unique_ptr<int> p = std::make_unique<int>(10); // 分配内存
    std::cout << "Value of *p: " << *p << std::endl;
    // 内存自动释放,不需要调用 delete
    return 0;
}

4.2 std::shared_ptr

std::shared_ptr 允许多个指针共享同一块内存,使用引用计数来管理内存。

#include <iostream>
#include <memory>

int main() {
    std::shared_ptr<int> p1 = std::make_shared<int>(10); // 分配内存
    std::shared_ptr<int> p2 = p1; // 共享所有权
    std::cout << "Value of *p1: " << *p1 << std::endl;
    std::cout << "Value of *p2: " << *p2 << std::endl;
    // 内存自动释放,当最后一个 shared_ptr 被销毁时
    return 0;
}

5. 内存泄漏与悬空指针

  • 内存泄漏:动态分配的内存未被释放,导致内存无法重用。
  • 悬空指针:指向已释放内存的指针,使用悬空指针可能导致未定义行为。

防止内存泄漏和悬空指针的常用方法包括:

  • 使用智能指针来自动管理内存。
  • 在释放内存后将指针设为 nullptr

6. 总结

C++ 中的内存管理是一个复杂但非常重要的主题。通过理解栈和堆的区别,正确使用 newdelete 运算符,以及引入智能指针,可以有效地管理内存,避免内存泄漏和悬空指针,提高程序的稳定性和性能。