C++ 内存管理:new/delete 与智能指针
C++ 允许程序员直接控制内存,这是它高效灵活的关键,但也容易引发内存泄漏、重复释放等严重问题。掌握 new/delete 和现代智能指针的正确用法,是写出安全可靠 C++ 代码的基础。
理解原始内存操作:new 与 delete
使用 new 动态分配对象时,程序会在堆(heap)上创建一个对象,并返回指向它的指针。例如:
int* p = new int(42);
这行代码在堆上分配了一个 int 类型的空间,并用值 42 初始化它,p 指向该地址。
必须成对使用 delete 释放内存。当你不再需要该对象时,调用 delete:
delete p;
忘记调用 delete 会导致内存泄漏——程序占用的内存不断增加,最终可能耗尽系统资源。
特别注意数组情况:如果你用 new[] 分配数组,必须用 delete[] 释放:
int* arr = new int[10];
// ... 使用 arr ...
delete[] arr; // 错误写法:delete arr; 会导致未定义行为
使用原始 new/delete 的主要风险在于:
- 忘记释放(内存泄漏)
- 多次释放同一指针(重复释放)
- 异常抛出导致
delete未执行(异常安全问题)
用智能指针自动管理内存
C++11 引入了智能指针,它们利用“资源获取即初始化”(RAII)技术,在对象生命周期结束时自动释放内存,彻底避免手动管理错误。
第一步:优先使用 std::unique_ptr
包含头文件 <memory>,然后声明 std::unique_ptr:
#include <memory>
std::unique_ptr<int> ptr = std::make_unique<int>(42);
std::unique_ptr 表示“独占所有权”——同一时间只能有一个 unique_ptr 指向该对象。当 ptr 离开作用域(如函数返回),其析构函数会自动调用 delete。
不要使用裸 new 创建智能指针,始终用 std::make_unique:
// 正确
auto ptr = std::make
// 错误(不推荐)
std::unique_ptr<int> ptr(new int(42));
后者在复杂表达式中可能因异常导致内存泄漏,而 make_unique 是异常安全的。
第二步:需要共享时使用 std::shared_ptr
当多个对象需要共享同一个资源时,使用 std::shared_ptr:
auto shared = std::make_shared<int>(42);
auto another = shared; // 引用计数变为 2
std::shared_ptr 内部维护一个引用计数。每当有新的 shared_ptr 指向同一对象,计数加一;当某个 shared_ptr 被销毁,计数减一。当计数归零时,自动释放内存。
同样,优先使用 std::make_shared 而非裸 new,原因同上。
第三步:配合 std::weak_ptr 打破循环引用
std::shared_ptr 的一个陷阱是循环引用:A 持有 B 的 shared_ptr,B 又持有 A 的 shared_ptr,导致引用计数永远不为零,内存无法释放。
引入 std::weak_ptr 解决此问题。它不增加引用计数,仅“观察”对象是否存在:
class Child;
class Parent {
public:
std::shared_ptr<Child> child;
};
class Child {
public:
std::weak_ptr<Parent> parent; // 使用 weak_ptr 避免循环
};
检查 weak_ptr 是否有效需先调用 lock():
if (auto p = child->parent.lock()) {
// p 是 shared_ptr,可安全使用
}
如何选择正确的工具
下表总结了不同场景下的推荐选择:
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 对象生命周期明确,仅一处使用 | std::unique_ptr |
零开销,自动释放,防止拷贝 |
| 多个所有者共享同一对象 | std::shared_ptr |
自动引用计数管理 |
| 需要观察对象但不参与所有权 | std::weak_ptr |
避免循环引用,不阻止销毁 |
| 性能极度敏感且能保证安全 | 裸指针 + 手动 new/delete |
仅限底层库或特殊优化,普通代码禁用 |
实战建议:彻底告别裸 new/delete
- 禁止在应用层代码中直接使用
new和delete。所有动态内存应通过智能指针管理。 - 函数参数和返回值优先使用智能指针。例如,返回新对象时返回
std::unique_ptr<T>,表明调用者获得所有权。 - 容器中存储智能指针而非裸指针。如
std::vector<std::unique_ptr<Base>>可安全管理多态对象集合。 - 警惕隐式转换。
shared_ptr可隐式转换为bool,但weak_ptr不行,需显式调用expired()或lock()。
// 安全的工厂函数示例
std::unique_ptr<Widget> createWidget(int type) {
if (type == 1) {
return std::make_unique<ConcreteWidgetA>();
}
return std::make_unique<ConcreteWidgetB>();
}
遵循这些规则,你的 C++ 程序将几乎不可能出现内存泄漏或双重释放错误。智能指针不是“高级特性”,而是现代 C++ 内存管理的标准实践。

暂无评论,快来抢沙发吧!