从 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 及之前,动态内存管理主要依赖 new
和 delete
。
这种方式灵活,但容易出错:
- 忘记
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++ 内存管理的演进过程,可以概括为:
- 手动管理:
new/delete
,灵活但危险。 - RAII:通过对象生命周期管理资源,解决异常安全问题。
- 智能指针:标准化 RAII,几乎消除了手动
delete
。 - 内存资源(PMR):提供可插拔的内存池机制,兼顾性能与灵活性。
现代 C++ 的推荐实践是:
- 优先使用智能指针(
std::unique_ptr
/std::shared_ptr
)。 - 在性能敏感场景下,考虑
std::pmr
或自定义分配器。
👉 这样,我们就从 裸指针 走到了 智能指针,再到 内存池,完整展示了 C++ 标准在内存管理上的演进。