CppStack

C++技术栈一站式学习 · ‌业精于勤,荒于嬉;行成于思,毁于随。

C++ 标准中的内存管理演进

Tags = [ C++Std ]

new/delete → RAII → 智能指针 → 内存资源(std::pmr

C++ 的内存管理经历了从手动 new/delete 到 RAII 的资源自动释放,再到 C++11 引入 std::unique_ptr、std::shared_ptr 等智能指针实现安全托管,最终在 C++17 提供 std::pmr 内存资源框架,支持可定制的内存池与高性能分配策略,逐步形成从底层控制到高层抽象的完整体系。


1. 手动管理:new / delete

在 C++98 及之前,动态内存管理主要依赖 newdelete
这种方式灵活,但容易出错:

  • 忘记 delete → 内存泄漏
  • 重复 delete → 未定义行为
  • 异常安全问题 → 提前退出时资源未释放

示例:手动管理

#include <iostream>

struct Foo {
    Foo(int v) : value(v) {
        std::cout << "Foo(" << value << ") constructed\n";
    }
    ~Foo() {
        std::cout << "Foo(" << value << ") destroyed\n";
    }
    int value;
};

int main() {
    Foo* p = new Foo(42);  // 手动分配
    std::cout << "Value = " << p->value << "\n";

    delete p;              // 手动释放
    return 0;
}

问题:如果 delete 被遗漏,内存就泄漏了;如果在 delete 前抛出异常,资源也无法正确释放。


2. RAII:资源获取即初始化

RAII(Resource Acquisition Is Initialization)是 C++ 的核心思想。
通过将资源(如内存、文件句柄)绑定到对象的生命周期,自动在析构函数中释放资源,从而避免手动管理的风险。

示例:RAII 封装

#include <iostream>

struct Foo {
    Foo(int v) : value(v) {
        std::cout << "Foo(" << value << ") constructed\n";
    }
    ~Foo() {
        std::cout << "Foo(" << value << ") destroyed\n";
    }
    int value;
};

class FooWrapper {
public:
    explicit FooWrapper(int v) : ptr(new Foo(v)) {}
    ~FooWrapper() { delete ptr; }

    Foo* operator->() { return ptr; }
    Foo& operator*() { return *ptr; }

private:
    Foo* ptr;
};

int main() {
    FooWrapper fw(100);  // 构造时分配
    std::cout << "Value = " << fw->value << "\n";
    // 析构时自动释放
    return 0;
}

RAII 解决了异常安全问题,但仍然需要手写封装类。
C++11 引入了 智能指针,将 RAII 思想标准化。


3. 智能指针:std::unique_ptr / std::shared_ptr

C++11 引入了智能指针,极大简化了内存管理。

  • std::unique_ptr:独占所有权,不能拷贝,只能移动。
  • std::shared_ptr:共享所有权,引用计数,最后一个持有者销毁时释放。

示例:智能指针

#include <iostream>
#include <memory>

struct Foo {
    Foo(int v) : value(v) {
        std::cout << "Foo(" << value << ") constructed\n";
    }
    ~Foo() {
        std::cout << "Foo(" << value << ") destroyed\n";
    }
    int value;
};

int main() {
    // 独占所有权
    std::unique_ptr<Foo> uptr = std::make_unique<Foo>(10);
    std::cout << "UniquePtr Value = " << uptr->value << "\n";

    // 共享所有权
    std::shared_ptr<Foo> sptr1 = std::make_shared<Foo>(20);
    std::shared_ptr<Foo> sptr2 = sptr1;  // 引用计数 +1
    std::cout << "SharedPtr Value = " << sptr2->value << "\n";

    return 0;
}

智能指针几乎消除了手动 delete 的需求,是现代 C++ 的推荐做法。
但在高性能场景下,频繁的堆分配和释放仍然可能成为瓶颈。
这时,C++17 引入了 内存资源(std::pmr


4. 内存池与 std::pmr(C++17)

C++17 引入了 Polymorphic Memory Resource (PMR),提供了一种可插拔的内存分配机制。
核心思想:

  • 容器(如 std::pmr::vector)可以使用自定义的内存资源。
  • 内存资源(std::pmr::memory_resource)可以实现内存池、堆分配、栈分配等策略。

示例:使用 std::pmr::monotonic_buffer_resource

#include <iostream>
#include <memory_resource>
#include <vector>

int main() {
    // 提前分配一块缓冲区
    char buffer[1024];
    std::pmr::monotonic_buffer_resource pool{buffer, sizeof(buffer)};

    // 使用内存池的 vector
    std::pmr::vector<int> vec{&pool};

    for (int i = 0; i < 10; ++i) {
        vec.push_back(i * i);
    }

    std::cout << "Vector contents: ";
    for (int v : vec) {
        std::cout << v << " ";
    }
    std::cout << "\n";

    // 注意:pool 的生命周期结束时,所有内存一次性释放
    return 0;
}

特点

  • monotonic_buffer_resource 是单调分配器,分配后不回收,直到整个资源销毁。
  • 适合临时对象批量分配的场景(如解析 JSON、编译器 AST 构建)。
  • 避免了频繁的 malloc/free,提升性能。

5. 总结

C++ 内存管理的演进过程,可以概括为:

  1. 手动管理new/delete,灵活但危险。
  2. RAII:通过对象生命周期管理资源,解决异常安全问题。
  3. 智能指针:标准化 RAII,几乎消除了手动 delete
  4. 内存资源(PMR):提供可插拔的内存池机制,兼顾性能与灵活性。

现代 C++ 的推荐实践是:

  • 优先使用智能指针std::unique_ptr / std::shared_ptr)。
  • 在性能敏感场景下,考虑 std::pmr 或自定义分配器。

👉 这样,我们就从 裸指针 走到了 智能指针,再到 内存池,完整展示了 C++ 标准在内存管理上的演进。