C++ 现代之路 (二):智能指针与资源革命——RAII 及现代智能指针深度剖析
资源用栈管理,动态资源交给智能指针,永远不要再写 new/delete。掌握了 RAII + 三大智能指针,你就跨过了现代 C++ 的第一道门槛,后面所有新特性(移动语义、完美转发、协程、Ranges……)都是在此基础上开出来的花。下一讲《C++ 现代之路 (三)》我们将进入“右值引用与移动语义”的工业级深度剖析——真正让 std::vector 扩容零拷贝、让大对象传递零开销的幕后英雄。敬请期待
C++ 现代之路 (二):智能指针与资源革命——RAII 及现代智能指针深度剖析
「真正把 C++ 从 C with Classes 带入现代语言殿堂的,不是 Lambda、不是概念、不是协程,而是 RAII 与智能指针。」
——Bjarne Stroustrup 在多个场合反复强调的“现代 C++ 的灵魂”
本讲对应《C++ 现代之路》系列第二讲,也是无数大厂 C++ 岗位面试必问的“生死线”话题:
只要你写过一个裸 new 却忘了 delete,面试官就能立刻把你 pass 掉。
一、为什么 C++ 以前那么容易内存泄漏?
void bad()
{
Foo* p = new Foo(); // 动态分配
do_something_that_may_throw();
// ……中间 100 行代码
delete p; // 这一行永远到不了
} // ← 泄漏 + 异常不安全
C 时代靠程序员自觉,C++98/03 靠“尽量记得写 delete”。
结果就是:所有稍大一点的项目都活在 Valgrind、ASan 的红色海洋里。
二、RAII:Resource Acquisition Is Initialization
RAII 是现代 C++ 的宗教级信条,可概括为 16 个字:
资源获取即初始化
生命周期由栈决定
析构函数绝不失约
void good()
{
std::vector<Foo> v; // 栈上对象
v.push_back(Foo()); // 资源获取
do_something_that_may_throw();
} // ← 离开作用域自动析构,绝不泄漏
RAII 消灭了 99% 的资源泄漏问题,剩下的 1% 交给智能指针。
三、C++11 三大智能指针:所有权模型的完备解
| 智能指针 | 所有权语义 | 拷贝 | 移动 | 典型场景 |
|---|---|---|---|---|
| unique_ptr | 独占所有权 | × | √ | 资源转移、返回值、容器元素 |
| shared_ptr | 共享所有权 | √ | √ | 需要多处共享、复杂图结构 |
| weak_ptr | 无所有权观察 | √ | √ | 打破循环引用、缓存 |
3.1 std::unique_ptr —— “现代 new/delete”
std::unique_ptr<Foo> p1 = std::make_unique<Foo>(1, 2); // C++14 推荐
auto p2 = std::move(p1); // 所有权转移,p1 变为空
// p1->use(); // 编译期就过不了,绝对安全
- 大小和裸指针一样(不牺牲性能)
- 支持自定义删除器(最常用在跨 DLL、文件句柄、锁等场景)
auto deleter = [](FILE* f){ if(f) fclose(f); };
std::unique_ptr<FILE, decltype(deleter)> fp(fopen("1.txt","r"), deleter);
3.2 std::shared_ptr —— 引用计数的全貌
std::shared_ptr<Foo> p1 = std::make_shared<Foo>(); // 一次分配控制块+对象
std::shared_ptr<Foo> p2 = p1; // 引用计数 2
std::weak_ptr<Foo> w = p1; // 不增加计数
控制块(control block)内部结构(简化版):
struct _ControlBlock {
std::atomic<long> strong_ref; // 1
std::atomic<long> weak_ref; // +1 (weak_ptr)
// deleter, allocator...
};
线程安全要点(面试必问):
- 引用计数的增减是原子操作,线程安全
- 控制块本身线程安全,但被管理对象本身不保证线程安全
- 不同 shared_ptr 实例指向同一对象是线程安全的(增减计数安全)
3.3 std::weak_ptr —— 打破循环引用的唯一解
经典循环引用导致永不释放:
struct Node {
std::shared_ptr<Node> next;
// std::shared_ptr<Node> prev; // 相互持有 → 泄漏!
std::weak_ptr<Node> prev; // 改成 weak_ptr
};
检查是否过期:
if (auto sp = w.lock()) {
sp->do_something();
}
};
### 四、make_unique / make_shared 为什么是强制要求?
```cpp
// 危险!可能泄漏
process(std::shared_ptr<Foo>(new Foo()), compute());
// 安全写法(C++14/17)
process(std::make_shared<Foo>(), compute());
process(std::make_unique<Foo>(), compute());
原因:异常安全。如果 compute() 抛异常,new Foo 的内存可能来不及交给 shared_ptr 接管。
make_shared 还有额外性能优势:一次分配控制块 + 对象,缓存命中率更高。
五、面试高频手撕题(现场必会)
- 实现一个简版 unique_ptr(支持移动、不支持自定义删除器)
template<typename T>
class MyUniquePtr {
T* ptr = nullptr;
public:
explicit MyUniquePtr(T* p = nullptr) : ptr(p) {}
~MyUniquePtr() { delete ptr; }
// 禁用拷贝
MyUniquePtr(const MyUniquePtr&) = delete;
MyUniquePtr& operator=(const MyUniquePtr&) = delete;
// 移动语义
MyUniquePtr(MyUniquePtr&& other) noexcept : ptr(other.ptr) {
other.ptr = nullptr;
}
MyUniquePtr& operator=(MyUniquePtr&& other) noexcept {
if (this != &other) {
delete ptr;
ptr = other.ptr;
other.ptr = nullptr;
}
return *this;
}
T* operator->() const { return ptr; }
T& operator*() const { return *ptr; }
T* get() const { return ptr; }
explicit operator bool() const { return ptr != nullptr; }
};
- 实现一个线程安全的简版 shared_ptr(只看思路)
- 用一个结构体包含 atomic strong, weak
- 构造时 new 控制块
- copy 时 atomic fetch_add
- 析构时 fetch_sub,若 strong 变为 0 则 delete 对象,再判断 weak 是否为 0 来 delete 控制块
六、在 UE5 大型项目中的真实编码规范(2025 最新)
// 1. 永远禁止裸 new/delete(Clang-Tidy + 编译器插件直接 error)
Foo* p = new Foo(); // 编译不过
delete p; // 编译不过
// 2. 返回值一律用 unique_ptr(所有权转移最清晰)
std::unique_ptr<AActor> SpawnMyActor() {
return MakeUnique<AActor>();
}
// 3. 容器里存 unique_ptr(节省一次堆分配)
TArray<std::unique_ptr<FComponent>> Components;
// 4. 需要共享的才用 shared_ptr,且必须 make_shared
TMap<FName, TSharedPtr<FConfig>> ConfigCache;
// 5. 父子关系用 TWeakObjectPtr(UE 自带,自动置空)
TWeakObjectPtr<AActor> Parent;
七、本讲总结:一句话记住现代 C++
资源用栈管理,动态资源交给智能指针,永远不要再写 new/delete。
掌握了 RAII + 三大智能指针,你就跨过了现代 C++ 的第一道门槛,后面所有新特性(移动语义、完美转发、协程、Ranges……)都是在此基础上开出来的花。
下一讲《C++ 现代之路 (三)》我们将进入“右值引用与移动语义”的工业级深度剖析——真正让 std::vector 扩容零拷贝、让大对象传递零开销的幕后英雄。
敬请期待。
更多推荐



所有评论(0)