现代C++智能指针解决的核心问题,不只是忘记delete造成的内存泄漏 ,更关键的是:把资源的所有权(Ownership)和对象生命周期(Object Lifetime)显式编码到类型系统中。
Resource → RAII → Ownership → Object Lifetime → unique_ptr / shared_ptr / weak_ptr
使用unique_ptr/shared_ptr表达所有权语义;在不需要共享所有权时,优先选择unique_ptr。
1. 从资源管理开始理解RAII
1.1 资源范围
程序中的"资源"范畴远不止new/delete、malloc/free管理的堆内存。凡是需要获取后必须手动释放的对象,都属于资源:
- 堆内存 Heap Memory
- 文件描述符 File Descriptor
- Socket套接字
- 互斥锁 Mutex
- 线程句柄 Thread
- 数据库连接 Database Connection
- GPU缓冲区 GPU Buffer
- 相机句柄 Camera Handle
- 设备句柄 Device Handle
- 共享内存 Shared Memory
资源通用生命周期模型:Acquire Resource → Use Resource → Release Resource
示例:文件与锁的原始写法
cpp
// 文件资源
FILE* f = fopen("data.txt", "r");
// 使用文件
fclose(f);
// 互斥锁资源
mutex.lock();
// 临界区业务
mutex.unlock();
裸管理资源的痛点:当代码提前return、抛出异常、存在多分支出口时,很容易跳过释放逻辑,造成资源泄漏。
cpp
void foo() {
int* p = new int[100];
if (something_wrong()) {
return; // 提前返回,内存泄漏,delete[]不会执行
}
delete[] p;
}
RAII就是专门解决这类问题的基础机制。
2. RAII:Resource Acquisition Is Initialization 资源获取即初始化
RAII核心思想:资源的生命周期绑定到普通C++栈对象的生命周期。
典型RAII封装模板:
cpp
class Resource {
public:
Resource() {
acquire(); // 构造阶段获取资源
}
~Resource() {
release(); // 析构阶段释放资源
}
};
使用示例:
cpp
void foo() {
Resource r;
// 使用资源
} // 离开作用域,r自动析构,资源自动释放
执行时序:
进入作用域 → 对象构造 → Acquire Resource → 使用资源 → 离开作用域 → 对象析构 → Release Resource
保障:无论函数正常返回,或是异常触发栈展开(stack unwinding),只要对象拥有自动存储期,析构函数一定会被调用。
当 throw 抛出异常,程序停止正常执行流,沿函数调用链向上回溯,依次析构各栈帧的自动存储对象,直到找到匹配 catch 处理器,该过程称为栈展开;若全程无匹配 catch,则调用 std::terminate 终止程序。
RAII本质:Resource Lifetime = Object Lifetime
智能指针,只是RAII在动态堆对象管理场景下的标准库实现。
3. 智能指针真正管理的是 Ownership(所有权)
所有权定义:谁负责决定对象的销毁时机、执行销毁动作。
裸指针T*仅表达:此处存放一个对象地址 ,但不携带任何所有权信息。
cpp
Robot* robot;
仅通过这一行类型声明,无法回答:
- 这个Robot对象是否由我创建?
- 是否由我负责调用delete销毁?
- 该指针只是临时访问(借用),还是持有对象?
- 是否有其他模块同时持有该对象?
- 对象什么时候销毁?
这就是所有权问题。
4. C++所有权关系
| 表达方式 | 所有权语义 |
|---|---|
| T(栈对象/值对象) | 对象自身拥有自己的生命周期 |
| T* / T& | 借用,非拥有访问 |
| std::unique_ptr | 独占所有权 |
| std::shared_ptr | 共享所有权 |
| std::weak_ptr | 非拥有,仅观察共享生命周期 |
示例:机器人系统,RobotSystem独占CameraDriver实例
cpp
class RobotSystem {
private:
std::unique_ptr<CameraDriver> camera_;
};
这段代码的语义不只是camera_是指针;而是RobotSystem独占CameraDriver对象的生命周期。
现代C++类型设计核心:Ownership本身就是接口语义,类型签名直接表达生命周期关系。
5. 所有权选择决策树
#mermaid-svg-zjtMS8bBtWGMBzBm{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-zjtMS8bBtWGMBzBm .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-zjtMS8bBtWGMBzBm .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-zjtMS8bBtWGMBzBm .error-icon{fill:#552222;}#mermaid-svg-zjtMS8bBtWGMBzBm .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-zjtMS8bBtWGMBzBm .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-zjtMS8bBtWGMBzBm .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-zjtMS8bBtWGMBzBm .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-zjtMS8bBtWGMBzBm .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-zjtMS8bBtWGMBzBm .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-zjtMS8bBtWGMBzBm .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-zjtMS8bBtWGMBzBm .marker{fill:#333333;stroke:#333333;}#mermaid-svg-zjtMS8bBtWGMBzBm .marker.cross{stroke:#333333;}#mermaid-svg-zjtMS8bBtWGMBzBm svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-zjtMS8bBtWGMBzBm p{margin:0;}#mermaid-svg-zjtMS8bBtWGMBzBm .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-zjtMS8bBtWGMBzBm .cluster-label text{fill:#333;}#mermaid-svg-zjtMS8bBtWGMBzBm .cluster-label span{color:#333;}#mermaid-svg-zjtMS8bBtWGMBzBm .cluster-label span p{background-color:transparent;}#mermaid-svg-zjtMS8bBtWGMBzBm .label text,#mermaid-svg-zjtMS8bBtWGMBzBm span{fill:#333;color:#333;}#mermaid-svg-zjtMS8bBtWGMBzBm .node rect,#mermaid-svg-zjtMS8bBtWGMBzBm .node circle,#mermaid-svg-zjtMS8bBtWGMBzBm .node ellipse,#mermaid-svg-zjtMS8bBtWGMBzBm .node polygon,#mermaid-svg-zjtMS8bBtWGMBzBm .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zjtMS8bBtWGMBzBm .rough-node .label text,#mermaid-svg-zjtMS8bBtWGMBzBm .node .label text,#mermaid-svg-zjtMS8bBtWGMBzBm .image-shape .label,#mermaid-svg-zjtMS8bBtWGMBzBm .icon-shape .label{text-anchor:middle;}#mermaid-svg-zjtMS8bBtWGMBzBm .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-zjtMS8bBtWGMBzBm .rough-node .label,#mermaid-svg-zjtMS8bBtWGMBzBm .node .label,#mermaid-svg-zjtMS8bBtWGMBzBm .image-shape .label,#mermaid-svg-zjtMS8bBtWGMBzBm .icon-shape .label{text-align:center;}#mermaid-svg-zjtMS8bBtWGMBzBm .node.clickable{cursor:pointer;}#mermaid-svg-zjtMS8bBtWGMBzBm .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-zjtMS8bBtWGMBzBm .arrowheadPath{fill:#333333;}#mermaid-svg-zjtMS8bBtWGMBzBm .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-zjtMS8bBtWGMBzBm .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-zjtMS8bBtWGMBzBm .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zjtMS8bBtWGMBzBm .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-zjtMS8bBtWGMBzBm .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zjtMS8bBtWGMBzBm .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-zjtMS8bBtWGMBzBm .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-zjtMS8bBtWGMBzBm .cluster text{fill:#333;}#mermaid-svg-zjtMS8bBtWGMBzBm .cluster span{color:#333;}#mermaid-svg-zjtMS8bBtWGMBzBm div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-zjtMS8bBtWGMBzBm .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-zjtMS8bBtWGMBzBm rect.text{fill:none;stroke-width:0;}#mermaid-svg-zjtMS8bBtWGMBzBm .icon-shape,#mermaid-svg-zjtMS8bBtWGMBzBm .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zjtMS8bBtWGMBzBm .icon-shape p,#mermaid-svg-zjtMS8bBtWGMBzBm .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-zjtMS8bBtWGMBzBm .icon-shape .label rect,#mermaid-svg-zjtMS8bBtWGMBzBm .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zjtMS8bBtWGMBzBm .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-zjtMS8bBtWGMBzBm .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-zjtMS8bBtWGMBzBm :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-zjtMS8bBtWGMBzBm .decision>*{fill:#E3F2FD!important;stroke:#1E88E5!important;stroke-width:1.5px!important;color:#0D47A1!important;}#mermaid-svg-zjtMS8bBtWGMBzBm .decision span{fill:#E3F2FD!important;stroke:#1E88E5!important;stroke-width:1.5px!important;color:#0D47A1!important;}#mermaid-svg-zjtMS8bBtWGMBzBm .decision tspan{fill:#0D47A1!important;}#mermaid-svg-zjtMS8bBtWGMBzBm .result>*{fill:#F1F8E9!important;stroke:#7CB342!important;stroke-width:1.5px!important;color:#33691E!important;}#mermaid-svg-zjtMS8bBtWGMBzBm .result span{fill:#F1F8E9!important;stroke:#7CB342!important;stroke-width:1.5px!important;color:#33691E!important;}#mermaid-svg-zjtMS8bBtWGMBzBm .result tspan{fill:#33691E!important;}#mermaid-svg-zjtMS8bBtWGMBzBm .ptr>*{fill:#FFF3E0!important;stroke:#FB8C00!important;stroke-width:1.5px!important;color:#E65100!important;}#mermaid-svg-zjtMS8bBtWGMBzBm .ptr span{fill:#FFF3E0!important;stroke:#FB8C00!important;stroke-width:1.5px!important;color:#E65100!important;}#mermaid-svg-zjtMS8bBtWGMBzBm .ptr tspan{fill:#E65100!important;}#mermaid-svg-zjtMS8bBtWGMBzBm .warn>*{fill:#FFEBEE!important;stroke:#E53935!important;stroke-width:1.5px!important;color:#B71C1C!important;}#mermaid-svg-zjtMS8bBtWGMBzBm .warn span{fill:#FFEBEE!important;stroke:#E53935!important;stroke-width:1.5px!important;color:#B71C1C!important;}#mermaid-svg-zjtMS8bBtWGMBzBm .warn tspan{fill:#B71C1C!important;} No
Yes
Yes
No
Yes
No
需要动态对象吗?
直接使用 T 值对象
是否只有一个 Owner?
unique_ptr
是否真的需要共享对象生命周期?
shared_ptr
重新设计所有权结构
工程原则:
- 默认优先值对象
T; - 确需动态生命周期,优先
unique_ptr; - 确需多个模块共同持有对象、共同延长生命周期,才使用
shared_ptr。
6. std::unique_ptr:独占所有权
6.1 基础语义
cpp
auto camera = std::make_unique<Camera>();
所有权关系:unique_ptr owns Camera,同一时刻仅允许存在一个所有者。
独占所有权禁止拷贝,以下代码编译失败:
cpp
std::unique_ptr<Camera> p1 = std::make_unique<Camera>();
std::unique_ptr<Camera> p2 = p1; // 编译报错,破坏独占语义
6.2 unique_ptr支持移动:转移所有权
禁止拷贝,但支持移动语义:
cpp
auto p2 = std::move(p1);
移动语义不是移动对象本身,只是转移所有权。
移动前后状态:
- Before:p1持有对象,p2为nullptr
- After:p1置为nullptr,p2接管对象。原堆上Camera对象地址、内容完全不变。
移动语义最重要的工程用途:资源所有权的安全转移。
6.3 unique_ptr将所有权编码进函数接口
cpp
// 函数签名语义:调用者必须将Camera所有权转移给该函数
void set_camera(std::unique_ptr<Camera> camera);
调用方式:
cpp
auto camera = std::make_unique<Camera>();
set_camera(std::move(camera));
// 调用后 camera == nullptr,所有权转移完成
如果仅需要访问对象、不需要接管所有权:
cpp
// 必须存在,借用访问
void process(Camera& camera);
// 可空,借用访问
void process(Camera* camera);
❌ 常见错误:不需要所有权,却传递std::unique_ptr<T>&
cpp
// 错误示例,函数不关心所有权,仅读取对象
void process(std::unique_ptr<Camera>& camera);
更合理写法:process(*camera)。
6.4 get()、release()、reset()
- get() 获取裸指针,所有权不发生任何变化。unique_ptr仍然持有对象,裸指针仅做观察借用,不能负责delete。
cpp
T* p = ptr.get();
- release() 剥离unique_ptr对资源的管理,unique_ptr立刻置空;所有权转移给裸指针。调用release之后,必须由接收裸指针的代码手动完成销毁,极易造成内存泄漏,属于高危接口。
cpp
T* p = ptr.release();
- reset() 推荐替代写法,更安全:
ptr = std::make_unique<Camera>();
cpp
ptr.reset(); // 销毁原有对象,ptr置空
ptr.reset(new Camera); // 销毁旧对象,接管新对象
6.5 unique_ptr 自定义删除器:通用RAII句柄
unique_ptr:可自定义销毁策略的独占RAII句柄 。
RAII不限于new/delete,可以封装任意操作系统句柄、SDK资源。
cpp
#include <cstdio>
#include <memory>
struct FileDeleter {
void operator()(FILE* file) const { //operator():把结构体变成仿函数(Functor),对象可以像函数一样被调用
if (file) {
std::fclose(file);
}
}
};
using FilePtr = std::unique_ptr<FILE, FileDeleter>;
int main() {
FilePtr file(std::fopen("data.txt", "r"));
if (!file) return 1;
// 文件操作
} // 离开作用域,自动调用FileDeleter执行fclose
std::unique_ptr<T, Deleter> 模板有两个参数:
- 第 1 个:管理的资源类型
FILE - 第 2 个:删除器类型,不是函数指针,是类型(这里是仿函数 FileDeleter)
相同范式可以封装Socket、CUDA句柄、OpenCV相机句柄、共享内存等资源。
6.6 unique_ptr运行时成本
std::unique_ptr<T>内部仅存储T*;当删除器是无状态类型时,编译器可利用空基类优化(EBO)消除额外内存开销 。
因此unique_ptr性能接近裸指针,这也是C++ Core Guidelines推荐优先使用它的原因。
如上面类6.5节struct operator()作为删除器(仿函数)unique_ptr也不会额外占用内存,但下面这个函数指针版本会多存一个函数指针,对象占两块指针大小,不如仿函数版本高效
cpp
using FilePtr = std::unique_ptr<FILE, int(*)(FILE*)>;
FilePtr file(fopen("data.txt","r"), fclose);
6.7 unique_ptr 与多态
cpp
class Driver {
public:
virtual ~Driver() = default; // 必须虚析构,保证多态销毁
};
class FanucDriver : public Driver {};
std::unique_ptr<Driver> driver = std::make_unique<FanucDriver>();
基类必须定义虚析构函数。否则通过unique_ptr<Base>销毁派生类对象会触发未定义行为。unique_ptr<Derived>可以隐式转为unique_ptr<Base>,默认删除器会按照Base类型析构。
6.8 unique_ptr 与 PImpl(Pointer to Implementation)
PImpl用于隔离头文件依赖、降低编译耦合、隐藏实现细节、稳定ABI。
cpp
// Camera.h
class Camera {
public:
Camera();
~Camera();
private:
class Impl;
std::unique_ptr<Impl> impl_;
};
头文件中不需要Impl完整定义,仅前置声明即可。
unique_ptr`支持不完整类型(incomplete type),但析构执行的位置,Impl类型必须是完整定义。
7. std::shared_ptr:共享所有权
当对象在同一时间拥有多个所有者,任意所有者都可以延长对象生命周期,使用shared_ptr。
cpp
Module A ──┐
Module B ──┼──> Object
Module C ──┘
7.1 shared_ptr实现
shared_ptr = 裸指针 + 引用计数,更重要的是包含独立控制块(Control Block)。
#mermaid-svg-4pSrZ31yMiQKR2bR{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-4pSrZ31yMiQKR2bR .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4pSrZ31yMiQKR2bR .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4pSrZ31yMiQKR2bR .error-icon{fill:#552222;}#mermaid-svg-4pSrZ31yMiQKR2bR .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4pSrZ31yMiQKR2bR .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4pSrZ31yMiQKR2bR .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4pSrZ31yMiQKR2bR .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4pSrZ31yMiQKR2bR .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4pSrZ31yMiQKR2bR .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4pSrZ31yMiQKR2bR .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4pSrZ31yMiQKR2bR .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4pSrZ31yMiQKR2bR .marker.cross{stroke:#333333;}#mermaid-svg-4pSrZ31yMiQKR2bR svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4pSrZ31yMiQKR2bR p{margin:0;}#mermaid-svg-4pSrZ31yMiQKR2bR .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-4pSrZ31yMiQKR2bR .cluster-label text{fill:#333;}#mermaid-svg-4pSrZ31yMiQKR2bR .cluster-label span{color:#333;}#mermaid-svg-4pSrZ31yMiQKR2bR .cluster-label span p{background-color:transparent;}#mermaid-svg-4pSrZ31yMiQKR2bR .label text,#mermaid-svg-4pSrZ31yMiQKR2bR span{fill:#333;color:#333;}#mermaid-svg-4pSrZ31yMiQKR2bR .node rect,#mermaid-svg-4pSrZ31yMiQKR2bR .node circle,#mermaid-svg-4pSrZ31yMiQKR2bR .node ellipse,#mermaid-svg-4pSrZ31yMiQKR2bR .node polygon,#mermaid-svg-4pSrZ31yMiQKR2bR .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-4pSrZ31yMiQKR2bR .rough-node .label text,#mermaid-svg-4pSrZ31yMiQKR2bR .node .label text,#mermaid-svg-4pSrZ31yMiQKR2bR .image-shape .label,#mermaid-svg-4pSrZ31yMiQKR2bR .icon-shape .label{text-anchor:middle;}#mermaid-svg-4pSrZ31yMiQKR2bR .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-4pSrZ31yMiQKR2bR .rough-node .label,#mermaid-svg-4pSrZ31yMiQKR2bR .node .label,#mermaid-svg-4pSrZ31yMiQKR2bR .image-shape .label,#mermaid-svg-4pSrZ31yMiQKR2bR .icon-shape .label{text-align:center;}#mermaid-svg-4pSrZ31yMiQKR2bR .node.clickable{cursor:pointer;}#mermaid-svg-4pSrZ31yMiQKR2bR .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-4pSrZ31yMiQKR2bR .arrowheadPath{fill:#333333;}#mermaid-svg-4pSrZ31yMiQKR2bR .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-4pSrZ31yMiQKR2bR .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-4pSrZ31yMiQKR2bR .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4pSrZ31yMiQKR2bR .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-4pSrZ31yMiQKR2bR .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4pSrZ31yMiQKR2bR .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-4pSrZ31yMiQKR2bR .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-4pSrZ31yMiQKR2bR .cluster text{fill:#333;}#mermaid-svg-4pSrZ31yMiQKR2bR .cluster span{color:#333;}#mermaid-svg-4pSrZ31yMiQKR2bR div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-4pSrZ31yMiQKR2bR .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-4pSrZ31yMiQKR2bR rect.text{fill:none;stroke-width:0;}#mermaid-svg-4pSrZ31yMiQKR2bR .icon-shape,#mermaid-svg-4pSrZ31yMiQKR2bR .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4pSrZ31yMiQKR2bR .icon-shape p,#mermaid-svg-4pSrZ31yMiQKR2bR .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-4pSrZ31yMiQKR2bR .icon-shape .label rect,#mermaid-svg-4pSrZ31yMiQKR2bR .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4pSrZ31yMiQKR2bR .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-4pSrZ31yMiQKR2bR .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-4pSrZ31yMiQKR2bR :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-4pSrZ31yMiQKR2bR .shared>*{fill:#E3F2FD!important;stroke:#1E88E5!important;stroke-width:1.5px!important;color:#0D47A1!important;}#mermaid-svg-4pSrZ31yMiQKR2bR .shared span{fill:#E3F2FD!important;stroke:#1E88E5!important;stroke-width:1.5px!important;color:#0D47A1!important;}#mermaid-svg-4pSrZ31yMiQKR2bR .shared tspan{fill:#0D47A1!important;}#mermaid-svg-4pSrZ31yMiQKR2bR .object>*{fill:#F1F8E9!important;stroke:#7CB342!important;stroke-width:1.5px!important;color:#33691E!important;}#mermaid-svg-4pSrZ31yMiQKR2bR .object span{fill:#F1F8E9!important;stroke:#7CB342!important;stroke-width:1.5px!important;color:#33691E!important;}#mermaid-svg-4pSrZ31yMiQKR2bR .object tspan{fill:#33691E!important;}#mermaid-svg-4pSrZ31yMiQKR2bR .control>*{fill:#FFF3E0!important;stroke:#FB8C00!important;stroke-width:1.5px!important;color:#E65100!important;}#mermaid-svg-4pSrZ31yMiQKR2bR .control span{fill:#FFF3E0!important;stroke:#FB8C00!important;stroke-width:1.5px!important;color:#E65100!important;}#mermaid-svg-4pSrZ31yMiQKR2bR .control tspan{fill:#E65100!important;} stored pointer
control block pointer
shared_ptr<T>
Object<T>
Control Block
Control Block存储:强引用计数、弱引用簿记信息、删除器、分配器信息等。
7.2 Strong Reference Count 强引用计数
多个shared_ptr共享对象所有权,最后一个所有者销毁时,销毁托管对象。
cpp
auto p1 = std::make_shared<Robot>();
auto p2 = p1;
auto p3 = p1;
p1/p2/p3共享同一个控制块,strong count = 3。
当调用p2.reset(),strong count递减;仅当强引用计数归零,托管对象才会被销毁。
7.3 shared_ptr内存布局
主流标准库实现中shared_ptr<T>包含两个指针:对象指针 + 控制块指针 。sizeof(std::shared_ptr<T>)通常等于2 * sizeof(void*)。
该尺寸属于实现细节,C++标准不保证固定大小,业务代码不应该依赖。
7.4 managed pointer 与 stored pointer:别名构造函数
shared_ptr的高级特性:托管对象,和get()返回的地址,可以不是同一个对象,由别名构造函数(aliasing constructor)实现。
cpp
struct Robot {
Motor motor;
};
auto robot = std::make_shared<Robot>();
// 别名构造
std::shared_ptr<Motor> motor(robot, &robot->motor);
此时:
- motor.get() = &robot->motor
- motor托管的所有权对象是Robot实例
只要motor这个shared_ptr存活,Robot对象就不会被销毁。
本质:shared_ptr管理的不是单个内存地址的引用计数,而是一整个所有权组(Ownership Group)。
7.5 优先使用 std::make_shared
cpp
std::shared_ptr<Robot> p(new Robot);
两次堆分配:①Robot对象内存;②Control Block内存。
推荐写法:
cpp
auto p = std::make_shared<Robot>();
主流实现将对象与控制块合并在单次堆分配,减少内存分配开销,提升缓存局部性。
7.6 make_shared的取舍权衡
cpp
auto p = std::make_shared<BigObject>();
std::weak_ptr<BigObject> weak = p;
p.reset();
p.reset()之后,strong count变为0,BigObject析构函数执行;但是因为weak_ptr仍然需要访问控制块,整块合并分配的内存空间无法立刻释放。
当对象体积巨大,且weak_ptr生命周期很长时,
make_shared会造成内存延迟释放,cppreference文档明确记录该特性。
8. std::weak_ptr:观察生命周期,不拥有对象
weak_ptr:对共享所有权对象的非拥有观察,不增加强引用计数。
cpp
auto shared = std::make_shared<Robot>();
std::weak_ptr<Robot> weak = shared;
weak_ptr仅记录控制块信息,不持有对象所有权,不能直接解引用访问对象。
8.1 weak_ptr不能直接解引用
cpp
*weak; // 非法编译
weak->run(); // 非法编译
原因:weak_ptr不拥有对象,其他线程可以随时销毁对象。访问对象前,必须调用lock()尝试提升为临时shared_ptr。
cpp
if (auto robot = weak.lock()) {
robot->run();
}
lock()是原子操作:对象存活返回有效shared_ptr;对象已销毁返回空shared_ptr。
8.2 反模式:expired() + lock()
cpp
// 错误写法,存在TOCTOU竞态
if (!weak.expired()) {
auto p = weak.lock();
}
expired()判断成功之后,lock()之前,其他线程可能释放最后一个shared_ptr,对象直接销毁。
✅ 正确做法:直接if(auto p = weak.lock()),一次原子检查+提升。
TOCTOU 竞态(Time Of Check To Time Of Use):在【条件检查】和【实际使用资源】之间,存在时间窗口,别的进程 / 线程修改了被检查的资源,导致检查结果失效。
9. 循环引用:所有权图设计错误
cpp
class A {
public:
std::shared_ptr<B> b;
};
class B {
public:
std::shared_ptr<A> a;
};
auto a = std::make_shared<A>();
auto b = std::make_shared<B>();
a->b = b;
b->a = a;
外部shared_ptr销毁后,A、B内部互相持有shared_ptr,强引用计数永远无法归零,造成内存泄漏。
循环引用不是shared_ptr的Bug,而是所有权图(Ownership Graph)出现环形依赖。
使用weak_ptr打破所有权环
业务语义如果是A拥有B,B仅观察A,则修改B的成员:
cpp
class B {
public:
std::weak_ptr<A> a;
};
A持有B的shared_ptr(所有权),B持有A的weak_ptr(观察),所有权图无环,对象可以正常析构。
10. enable_shared_from_this
场景:对象内部回调,需要获取指向自身的shared_ptr。
错误写法:裸指针直接构造shared_ptr
cpp
// 危险代码
std::shared_ptr<Session>(this);
当外部已经使用make_shared管理Session时,shared_ptr(this)会创建全新独立的控制块。两个控制块互不感知,最终会对同一个对象执行两次delete,触发double delete,未定义行为。cppreference明确禁止该用法。
正确方案:继承std::enable_shared_from_this
cpp
class Session : public std::enable_shared_from_this<Session> {
public:
std::shared_ptr<Session> get_self() {
return shared_from_this();
}
};
调用:
cpp
auto p1 = std::make_shared<Session>();
auto p2 = p1->get_self();
p1与p2共享同一个控制块,不会重复析构。
限制:
shared_from_this()调用前,对象必须已经被shared_ptr托管 ;直接栈实例调用会抛出std::bad_weak_ptr异常。enable_shared_from_this内部维护弱引用,用于定位已存在的控制块。
11. Storage 与 Object:区分存储空间与对象
C++内存模型中,存储(Storage) ≠ 对象(Object)。
cpp
// 分配原始内存,只有存储空间,没有Robot对象
void* memory = ::operator new(sizeof(Robot));
原始内存只是一块Raw Storage,内存地址存在,但没有构造出有效的Robot对象 。
使用placement new在存储上创建对象:
cpp
Robot* robot = new(memory) Robot();
完整生命周期时序:
Allocate Storage → Construct Object → Object Lifetime Begins → 使用对象 → Destroy Object → Object Lifetime Ends → Deallocate Storage
new / delete的两层语义
cpp
new Robot;
operator new:分配原始存储- 调用Robot构造函数,在存储上创建对象
coo
delete robot;
- 调用Robot析构函数,结束对象生命周期
operator delete释放底层存储空间
内存分配 ≠ 对象构造;对象析构 ≠ 内存释放。
construct_at / destroy_at 显式控制对象生命周期
C++17提供std::destroy_at,C++20新增std::construct_at,用于手动在原始内存上构造、销毁对象,不操作存储分配。
cpp
#include <memory>
#include <memory>
struct Robot {
Robot(int id) {}
};
int main() {
std::allocator<Robot> alloc;
Robot* memory = alloc.allocate(1);
std::construct_at(memory, 1); // 构造对象,对象生命周期启动
std::destroy_at(memory); // 销毁对象,对象生命周期结束
alloc.deallocate(memory, 1); // 释放底层存储
}
12. 野指针Dangling Pointer本质
cpp
int* p;
{
int x = 10;
p = &x;
}
std::cout << *p; // 未定义行为
作用域结束,局部变量x的对象生命周期终止。指针p仍然保存原来的地址值,但是该地址不再存在有效的int对象。
野指针定义:指针保存有效内存地址,但该地址上不存在存活的C++对象。地址存在,不等于对象存活。
13. 智能指针作为函数参数:用接口表达所有权
现代C++函数签名 应当直接体现所有权语义:
| 函数签名 | 语义描述 |
|---|---|
void process(Robot& robot); |
借用访问,对象必须有效,不接管所有权 |
void process(Robot* robot); |
借用访问,可空,不接管所有权 |
void set_robot(std::unique_ptr<Robot> robot); |
所有权转移,函数接管对象 |
void register_robot(std::shared_ptr<Robot> robot); |
获取共享所有权,函数可延长对象生命周期 |
void observe(std::weak_ptr<Robot> robot); |
仅观察共享生命周期,不延长对象存活时间 |
C++ Core Guidelines:仅当接口需要表达生命周期/所有权语义时,才在参数中使用智能指针。
反模式:不必要传递shared_ptr
所有权属于对象存储层,单纯读取业务逻辑,不应该依赖智能指针类型。
cpp
// 错误示例:仅读取对象,却传shared_ptr常引用
void detect(const std::shared_ptr<Image>& image) {
image->width();
image->height();
}
函数内部不保存shared_ptr,不需要共享所有权。应改为:
cpp
void detect(const Image& image);
// 调用 detect(*image_ptr);
14. shared_ptr线程安全三层边界
shared_ptr不是天然线程安全
- ✅ Control Block(控制块)并发安全
多个线程操作不同shared_ptr副本,并发修改同一个控制块的引用计数是安全的,标准保证引用计数操作原子化。
cpp
auto p = std::make_shared<Robot>();
auto p1 = p, p2 = p;
// 线程A:p1.reset(); 线程B:p2.reset(); 安全
- ❌ 同一个shared_ptr对象并发修改不安全
多个线程同时读写同一个shared_ptr实例,存在数据竞争。
cpp
std::shared_ptr<Robot> p;
// 线程A:p = std::make_shared<Robot>();
// 线程B:p.reset();
// 无同步,触发Data Race
C++20提供std::atomic<std::shared_ptr<T>>用于该场景。
std::atomic<std::shared_ptr<T>> 原子包装 shared_ptr,保证对这个指针本身的读写是原子操作,解决多线程下裸指针 / 普通 shared_ptr 读写的竞争问题。
cpp
std::atomic<std::shared_ptr<Robot>> current_robot; //声明一个原子变量,包裹 shared_ptr<Robot>
current_robot.store(std::make_shared<Robot>());
auto robot = current_robot.load();
-
store:原子写 ,把新的shared_ptr存入原子变量。替换当前持有的 Robot 句柄。- 写入成功后,
current_robot指向新 Robot; - 原来旧的
shared_ptr会被释放,旧 Robot 的引用计数减一,如果没人持有就析构。
- 写入成功后,
-
load:原子读 ,原子取出当前原子变量里保存的shared_ptr,拷贝一份给局部变量robot。
- ❌ 托管对象本身不自动线程安全
shared_ptr仅保证对象生命周期安全,不保护对象内部成员并发读写。
cpp
auto robot = std::make_shared<Robot>();
// 线程A:robot->x = 10;
// 线程B:robot->x = 20;
// 存在数据竞争,需要mutex/atomic等同步原语
控制块安全 ≠ shared_ptr实例安全 ≠ 托管业务对象安全。
15. shared_ptr性能开销
对比unique_ptr,shared_ptr存在额外开销:
- 控制块额外堆内存;
- 强引用计数的原子同步指令;
- 额外内存访问;
- 多核场景下缓存行颠簸(cache contention),多个CPU核反复同步同一个缓存行。
shared_ptr不是"大号指针",它是一套完整共享生命周期协议。
use_count()不可用于并发逻辑
cpp
if (ptr.use_count() == 1) {
// 假设我是唯一所有者
}
并发环境下该判断不可靠,读取use_count之后,其他线程可以立刻复制shared_ptr,计数瞬间改变。
use_count()仅适用于调试、诊断打印,不能作为同步协议判断条件。
16. 常见错误汇总
- 裸指针多次构造shared_ptr:同一个原始裸指针创建多个shared_ptr,生成多份独立控制块,最终double delete。
shared_ptr<T>(this):从this裸指针创建shared_ptr,多控制块,未定义行为,优先enable_shared_from_this。- 滥用release():剥离unique_ptr所有权,丢失RAII保护,裸指针忘记释放造成泄漏。
- 全系统无脑shared_ptr:所有权图环形依赖,不断增加weak_ptr打补丁,本质是顶层对象生命周期架构设计失效。
17. 工程所有权设计范例:机器人系统
推荐树形所有权结构,使用unique_ptr构建清晰的所有权树,无引用计数,析构顺序确定。
cpp
class RobotSystem {
private:
std::unique_ptr<CameraDriver> camera_;
std::unique_ptr<RobotDriver> robot_;
std::unique_ptr<Planner> planner_;
std::unique_ptr<Controller> controller_;
};
所有权树:
cpp
RobotSystem
├─ CameraDriver
├─ RobotDriver
├─ Planner
└─ Controller
RobotSystem析构时,成员按逆序析构,所有子资源自动释放,生命周期边界清晰。
shared_ptr适用场景
仅当对象无法自然归属单一所有者时使用,典型场景:异步回调、多线程任务,多个执行单元都需要保证Session对象在回调执行期间存活。
unique_ptr适合树形所有权;shared_ptr适合真正的共享所有权图。
18. 裸指针并未被现代C++淘汰
现代C++不是:裸指针=坏,智能指针=好。
区分是拥有指针(Owning Pointer)与非拥有指针(Non-owning Pointer) 。
Camera* camera;,当语义明确为:仅观察、不负责销毁,是完全合法的写法。
C++ Core Guidelines反对的是:使用裸指针隐式传递所有权,调用者无法判断销毁责任归属。
19. 所有权分析思维模型
看到对象,不要第一反应:该选unique_ptr还是shared_ptr。
先依次回答问题:
- 谁拥有这个对象?
- 谁决定它的销毁生命周期?
- 所有权是否可以转移?
- 是否真的存在多个所有者?
- 该指针仅仅是观察借用吗?
推导结果:
- 值对象 → T
- 单一所有者 → unique_ptr
- 多个所有者 → shared_ptr
- 观察共享生命周期 → weak_ptr
- 普通借用访问 → T& / T*
- 能直接使用值对象,就不要动态分配。优先
Robot robot;,而非make_unique;动态分配必须有合理理由。 - 动态对象,默认选择
unique_ptr作为所有权默认方案。 - 只有真实存在共享所有权场景,才使用shared_ptr;不要用shared_ptr掩盖生命周期设计缺陷。
- 观察者不应当持有对象所有权:单纯访问优先
T&/T*;观察共享对象生命周期使用weak_ptr。 - 所有权语义直接体现在API签名,阅读接口即可判断对象生命周期关系。
- 禁止从已经被智能指针托管的裸指针重新创建shared_ptr,尤其
shared_ptr(this)。 - shared_ptr只保证对象生命周期安全,不会自动保证托管对象的并发访问安全,内部数据访问仍然需要同步原语。
智能指针体系
#mermaid-svg-HxvhrFNg9Y5yscJb{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-HxvhrFNg9Y5yscJb .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HxvhrFNg9Y5yscJb .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HxvhrFNg9Y5yscJb .error-icon{fill:#552222;}#mermaid-svg-HxvhrFNg9Y5yscJb .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HxvhrFNg9Y5yscJb .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HxvhrFNg9Y5yscJb .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HxvhrFNg9Y5yscJb .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HxvhrFNg9Y5yscJb .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HxvhrFNg9Y5yscJb .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HxvhrFNg9Y5yscJb .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HxvhrFNg9Y5yscJb .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HxvhrFNg9Y5yscJb .marker.cross{stroke:#333333;}#mermaid-svg-HxvhrFNg9Y5yscJb svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HxvhrFNg9Y5yscJb p{margin:0;}#mermaid-svg-HxvhrFNg9Y5yscJb .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-HxvhrFNg9Y5yscJb .cluster-label text{fill:#333;}#mermaid-svg-HxvhrFNg9Y5yscJb .cluster-label span{color:#333;}#mermaid-svg-HxvhrFNg9Y5yscJb .cluster-label span p{background-color:transparent;}#mermaid-svg-HxvhrFNg9Y5yscJb .label text,#mermaid-svg-HxvhrFNg9Y5yscJb span{fill:#333;color:#333;}#mermaid-svg-HxvhrFNg9Y5yscJb .node rect,#mermaid-svg-HxvhrFNg9Y5yscJb .node circle,#mermaid-svg-HxvhrFNg9Y5yscJb .node ellipse,#mermaid-svg-HxvhrFNg9Y5yscJb .node polygon,#mermaid-svg-HxvhrFNg9Y5yscJb .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HxvhrFNg9Y5yscJb .rough-node .label text,#mermaid-svg-HxvhrFNg9Y5yscJb .node .label text,#mermaid-svg-HxvhrFNg9Y5yscJb .image-shape .label,#mermaid-svg-HxvhrFNg9Y5yscJb .icon-shape .label{text-anchor:middle;}#mermaid-svg-HxvhrFNg9Y5yscJb .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-HxvhrFNg9Y5yscJb .rough-node .label,#mermaid-svg-HxvhrFNg9Y5yscJb .node .label,#mermaid-svg-HxvhrFNg9Y5yscJb .image-shape .label,#mermaid-svg-HxvhrFNg9Y5yscJb .icon-shape .label{text-align:center;}#mermaid-svg-HxvhrFNg9Y5yscJb .node.clickable{cursor:pointer;}#mermaid-svg-HxvhrFNg9Y5yscJb .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-HxvhrFNg9Y5yscJb .arrowheadPath{fill:#333333;}#mermaid-svg-HxvhrFNg9Y5yscJb .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-HxvhrFNg9Y5yscJb .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-HxvhrFNg9Y5yscJb .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HxvhrFNg9Y5yscJb .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-HxvhrFNg9Y5yscJb .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HxvhrFNg9Y5yscJb .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-HxvhrFNg9Y5yscJb .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-HxvhrFNg9Y5yscJb .cluster text{fill:#333;}#mermaid-svg-HxvhrFNg9Y5yscJb .cluster span{color:#333;}#mermaid-svg-HxvhrFNg9Y5yscJb div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-HxvhrFNg9Y5yscJb .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-HxvhrFNg9Y5yscJb rect.text{fill:none;stroke-width:0;}#mermaid-svg-HxvhrFNg9Y5yscJb .icon-shape,#mermaid-svg-HxvhrFNg9Y5yscJb .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HxvhrFNg9Y5yscJb .icon-shape p,#mermaid-svg-HxvhrFNg9Y5yscJb .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-HxvhrFNg9Y5yscJb .icon-shape .label rect,#mermaid-svg-HxvhrFNg9Y5yscJb .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HxvhrFNg9Y5yscJb .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-HxvhrFNg9Y5yscJb .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-HxvhrFNg9Y5yscJb :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-HxvhrFNg9Y5yscJb .root>*{fill:#E3F2FD!important;stroke:#1E88E5!important;stroke-width:2px!important;color:#0D47A1!important;}#mermaid-svg-HxvhrFNg9Y5yscJb .root span{fill:#E3F2FD!important;stroke:#1E88E5!important;stroke-width:2px!important;color:#0D47A1!important;}#mermaid-svg-HxvhrFNg9Y5yscJb .root tspan{fill:#0D47A1!important;}#mermaid-svg-HxvhrFNg9Y5yscJb .decision>*{fill:#FFF8E1!important;stroke:#FFB300!important;stroke-width:1.5px!important;color:#E65100!important;}#mermaid-svg-HxvhrFNg9Y5yscJb .decision span{fill:#FFF8E1!important;stroke:#FFB300!important;stroke-width:1.5px!important;color:#E65100!important;}#mermaid-svg-HxvhrFNg9Y5yscJb .decision tspan{fill:#E65100!important;}#mermaid-svg-HxvhrFNg9Y5yscJb .owner>*{fill:#F3E5F5!important;stroke:#8E24AA!important;stroke-width:1.5px!important;color:#4A148C!important;}#mermaid-svg-HxvhrFNg9Y5yscJb .owner span{fill:#F3E5F5!important;stroke:#8E24AA!important;stroke-width:1.5px!important;color:#4A148C!important;}#mermaid-svg-HxvhrFNg9Y5yscJb .owner tspan{fill:#4A148C!important;}#mermaid-svg-HxvhrFNg9Y5yscJb .ptr>*{fill:#E8F5E9!important;stroke:#43A047!important;stroke-width:1.5px!important;color:#1B5E20!important;}#mermaid-svg-HxvhrFNg9Y5yscJb .ptr span{fill:#E8F5E9!important;stroke:#43A047!important;stroke-width:1.5px!important;color:#1B5E20!important;}#mermaid-svg-HxvhrFNg9Y5yscJb .ptr tspan{fill:#1B5E20!important;}#mermaid-svg-HxvhrFNg9Y5yscJb .control>*{fill:#FFF3E0!important;stroke:#FB8C00!important;stroke-width:1.5px!important;color:#E65100!important;}#mermaid-svg-HxvhrFNg9Y5yscJb .control span{fill:#FFF3E0!important;stroke:#FB8C00!important;stroke-width:1.5px!important;color:#E65100!important;}#mermaid-svg-HxvhrFNg9Y5yscJb .control tspan{fill:#E65100!important;}#mermaid-svg-HxvhrFNg9Y5yscJb .alive>*{fill:#E8F5E9!important;stroke:#43A047!important;stroke-width:1.5px!important;color:#1B5E20!important;}#mermaid-svg-HxvhrFNg9Y5yscJb .alive span{fill:#E8F5E9!important;stroke:#43A047!important;stroke-width:1.5px!important;color:#1B5E20!important;}#mermaid-svg-HxvhrFNg9Y5yscJb .alive tspan{fill:#1B5E20!important;}#mermaid-svg-HxvhrFNg9Y5yscJb .destroy>*{fill:#FFEBEE!important;stroke:#E53935!important;stroke-width:1.5px!important;color:#B71C1C!important;}#mermaid-svg-HxvhrFNg9Y5yscJb .destroy span{fill:#FFEBEE!important;stroke:#E53935!important;stroke-width:1.5px!important;color:#B71C1C!important;}#mermaid-svg-HxvhrFNg9Y5yscJb .destroy tspan{fill:#B71C1C!important;}#mermaid-svg-HxvhrFNg9Y5yscJb .keep>*{fill:#E1F5FE!important;stroke:#039BE5!important;stroke-width:1.5px!important;color:#01579B!important;}#mermaid-svg-HxvhrFNg9Y5yscJb .keep span{fill:#E1F5FE!important;stroke:#039BE5!important;stroke-width:1.5px!important;color:#01579B!important;}#mermaid-svg-HxvhrFNg9Y5yscJb .keep tspan{fill:#01579B!important;}#mermaid-svg-HxvhrFNg9Y5yscJb .release>*{fill:#FCE4EC!important;stroke:#D81B60!important;stroke-width:1.5px!important;color:#880E4F!important;}#mermaid-svg-HxvhrFNg9Y5yscJb .release span{fill:#FCE4EC!important;stroke:#D81B60!important;stroke-width:1.5px!important;color:#880E4F!important;}#mermaid-svg-HxvhrFNg9Y5yscJb .release tspan{fill:#880E4F!important;}#mermaid-svg-HxvhrFNg9Y5yscJb .note>*{fill:#F5F5F5!important;stroke:#9E9E9E!important;stroke-width:1px!important;color:#424242!important;}#mermaid-svg-HxvhrFNg9Y5yscJb .note span{fill:#F5F5F5!important;stroke:#9E9E9E!important;stroke-width:1px!important;color:#424242!important;}#mermaid-svg-HxvhrFNg9Y5yscJb .note tspan{fill:#424242!important;} Single Owner
Multiple Owners
Yes
No
C++ Object
Who owns its lifetime?
Single Owner
Multiple Owners
unique_ptr<T>
shared_ptr<T>
move transfers ownership
shared control block
strong count
count > 0
count == 0
Object Alive
Destroy Object
Control Block
weak observers?
Keep Control Block
Release Control Block
#mermaid-svg-0iM25VMuZjshZ3Yu{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-0iM25VMuZjshZ3Yu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-0iM25VMuZjshZ3Yu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-0iM25VMuZjshZ3Yu .error-icon{fill:#552222;}#mermaid-svg-0iM25VMuZjshZ3Yu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-0iM25VMuZjshZ3Yu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-0iM25VMuZjshZ3Yu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-0iM25VMuZjshZ3Yu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-0iM25VMuZjshZ3Yu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-0iM25VMuZjshZ3Yu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-0iM25VMuZjshZ3Yu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-0iM25VMuZjshZ3Yu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-0iM25VMuZjshZ3Yu .marker.cross{stroke:#333333;}#mermaid-svg-0iM25VMuZjshZ3Yu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-0iM25VMuZjshZ3Yu p{margin:0;}#mermaid-svg-0iM25VMuZjshZ3Yu .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-0iM25VMuZjshZ3Yu .cluster-label text{fill:#333;}#mermaid-svg-0iM25VMuZjshZ3Yu .cluster-label span{color:#333;}#mermaid-svg-0iM25VMuZjshZ3Yu .cluster-label span p{background-color:transparent;}#mermaid-svg-0iM25VMuZjshZ3Yu .label text,#mermaid-svg-0iM25VMuZjshZ3Yu span{fill:#333;color:#333;}#mermaid-svg-0iM25VMuZjshZ3Yu .node rect,#mermaid-svg-0iM25VMuZjshZ3Yu .node circle,#mermaid-svg-0iM25VMuZjshZ3Yu .node ellipse,#mermaid-svg-0iM25VMuZjshZ3Yu .node polygon,#mermaid-svg-0iM25VMuZjshZ3Yu .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-0iM25VMuZjshZ3Yu .rough-node .label text,#mermaid-svg-0iM25VMuZjshZ3Yu .node .label text,#mermaid-svg-0iM25VMuZjshZ3Yu .image-shape .label,#mermaid-svg-0iM25VMuZjshZ3Yu .icon-shape .label{text-anchor:middle;}#mermaid-svg-0iM25VMuZjshZ3Yu .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-0iM25VMuZjshZ3Yu .rough-node .label,#mermaid-svg-0iM25VMuZjshZ3Yu .node .label,#mermaid-svg-0iM25VMuZjshZ3Yu .image-shape .label,#mermaid-svg-0iM25VMuZjshZ3Yu .icon-shape .label{text-align:center;}#mermaid-svg-0iM25VMuZjshZ3Yu .node.clickable{cursor:pointer;}#mermaid-svg-0iM25VMuZjshZ3Yu .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-0iM25VMuZjshZ3Yu .arrowheadPath{fill:#333333;}#mermaid-svg-0iM25VMuZjshZ3Yu .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-0iM25VMuZjshZ3Yu .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-0iM25VMuZjshZ3Yu .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0iM25VMuZjshZ3Yu .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-0iM25VMuZjshZ3Yu .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0iM25VMuZjshZ3Yu .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-0iM25VMuZjshZ3Yu .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-0iM25VMuZjshZ3Yu .cluster text{fill:#333;}#mermaid-svg-0iM25VMuZjshZ3Yu .cluster span{color:#333;}#mermaid-svg-0iM25VMuZjshZ3Yu div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-0iM25VMuZjshZ3Yu .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-0iM25VMuZjshZ3Yu rect.text{fill:none;stroke-width:0;}#mermaid-svg-0iM25VMuZjshZ3Yu .icon-shape,#mermaid-svg-0iM25VMuZjshZ3Yu .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0iM25VMuZjshZ3Yu .icon-shape p,#mermaid-svg-0iM25VMuZjshZ3Yu .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-0iM25VMuZjshZ3Yu .icon-shape .label rect,#mermaid-svg-0iM25VMuZjshZ3Yu .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0iM25VMuZjshZ3Yu .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-0iM25VMuZjshZ3Yu .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-0iM25VMuZjshZ3Yu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-0iM25VMuZjshZ3Yu .nonown>*{fill:#EDE7F6!important;stroke:#5E35B1!important;stroke-width:1.5px!important;color:#311B92!important;}#mermaid-svg-0iM25VMuZjshZ3Yu .nonown span{fill:#EDE7F6!important;stroke:#5E35B1!important;stroke-width:1.5px!important;color:#311B92!important;}#mermaid-svg-0iM25VMuZjshZ3Yu .nonown tspan{fill:#311B92!important;}#mermaid-svg-0iM25VMuZjshZ3Yu .note>*{fill:#F5F5F5!important;stroke:#9E9E9E!important;stroke-width:1px!important;color:#424242!important;}#mermaid-svg-0iM25VMuZjshZ3Yu .note span{fill:#F5F5F5!important;stroke:#9E9E9E!important;stroke-width:1px!important;color:#424242!important;}#mermaid-svg-0iM25VMuZjshZ3Yu .note tspan{fill:#424242!important;} Non-owning relations
T& / T*
ordinary borrow
weak_ptr<T>
observe shared ownership
lock() → temporary shared_ptr
C++内存体系
#mermaid-svg-129OZZhktrS5xKw7{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-129OZZhktrS5xKw7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-129OZZhktrS5xKw7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-129OZZhktrS5xKw7 .error-icon{fill:#552222;}#mermaid-svg-129OZZhktrS5xKw7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-129OZZhktrS5xKw7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-129OZZhktrS5xKw7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-129OZZhktrS5xKw7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-129OZZhktrS5xKw7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-129OZZhktrS5xKw7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-129OZZhktrS5xKw7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-129OZZhktrS5xKw7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-129OZZhktrS5xKw7 .marker.cross{stroke:#333333;}#mermaid-svg-129OZZhktrS5xKw7 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-129OZZhktrS5xKw7 p{margin:0;}#mermaid-svg-129OZZhktrS5xKw7 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-129OZZhktrS5xKw7 .cluster-label text{fill:#333;}#mermaid-svg-129OZZhktrS5xKw7 .cluster-label span{color:#333;}#mermaid-svg-129OZZhktrS5xKw7 .cluster-label span p{background-color:transparent;}#mermaid-svg-129OZZhktrS5xKw7 .label text,#mermaid-svg-129OZZhktrS5xKw7 span{fill:#333;color:#333;}#mermaid-svg-129OZZhktrS5xKw7 .node rect,#mermaid-svg-129OZZhktrS5xKw7 .node circle,#mermaid-svg-129OZZhktrS5xKw7 .node ellipse,#mermaid-svg-129OZZhktrS5xKw7 .node polygon,#mermaid-svg-129OZZhktrS5xKw7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-129OZZhktrS5xKw7 .rough-node .label text,#mermaid-svg-129OZZhktrS5xKw7 .node .label text,#mermaid-svg-129OZZhktrS5xKw7 .image-shape .label,#mermaid-svg-129OZZhktrS5xKw7 .icon-shape .label{text-anchor:middle;}#mermaid-svg-129OZZhktrS5xKw7 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-129OZZhktrS5xKw7 .rough-node .label,#mermaid-svg-129OZZhktrS5xKw7 .node .label,#mermaid-svg-129OZZhktrS5xKw7 .image-shape .label,#mermaid-svg-129OZZhktrS5xKw7 .icon-shape .label{text-align:center;}#mermaid-svg-129OZZhktrS5xKw7 .node.clickable{cursor:pointer;}#mermaid-svg-129OZZhktrS5xKw7 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-129OZZhktrS5xKw7 .arrowheadPath{fill:#333333;}#mermaid-svg-129OZZhktrS5xKw7 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-129OZZhktrS5xKw7 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-129OZZhktrS5xKw7 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-129OZZhktrS5xKw7 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-129OZZhktrS5xKw7 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-129OZZhktrS5xKw7 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-129OZZhktrS5xKw7 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-129OZZhktrS5xKw7 .cluster text{fill:#333;}#mermaid-svg-129OZZhktrS5xKw7 .cluster span{color:#333;}#mermaid-svg-129OZZhktrS5xKw7 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-129OZZhktrS5xKw7 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-129OZZhktrS5xKw7 rect.text{fill:none;stroke-width:0;}#mermaid-svg-129OZZhktrS5xKw7 .icon-shape,#mermaid-svg-129OZZhktrS5xKw7 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-129OZZhktrS5xKw7 .icon-shape p,#mermaid-svg-129OZZhktrS5xKw7 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-129OZZhktrS5xKw7 .icon-shape .label rect,#mermaid-svg-129OZZhktrS5xKw7 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-129OZZhktrS5xKw7 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-129OZZhktrS5xKw7 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-129OZZhktrS5xKw7 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-129OZZhktrS5xKw7 .os>*{fill:#E3F2FD!important;stroke:#1E88E5!important;stroke-width:1.5px!important;color:#0D47A1!important;}#mermaid-svg-129OZZhktrS5xKw7 .os span{fill:#E3F2FD!important;stroke:#1E88E5!important;stroke-width:1.5px!important;color:#0D47A1!important;}#mermaid-svg-129OZZhktrS5xKw7 .os tspan{fill:#0D47A1!important;}#mermaid-svg-129OZZhktrS5xKw7 .mem>*{fill:#E1F5FE!important;stroke:#039BE5!important;stroke-width:1.5px!important;color:#01579B!important;}#mermaid-svg-129OZZhktrS5xKw7 .mem span{fill:#E1F5FE!important;stroke:#039BE5!important;stroke-width:1.5px!important;color:#01579B!important;}#mermaid-svg-129OZZhktrS5xKw7 .mem tspan{fill:#01579B!important;}#mermaid-svg-129OZZhktrS5xKw7 .alloc>*{fill:#FFF3E0!important;stroke:#FB8C00!important;stroke-width:1.5px!important;color:#E65100!important;}#mermaid-svg-129OZZhktrS5xKw7 .alloc span{fill:#FFF3E0!important;stroke:#FB8C00!important;stroke-width:1.5px!important;color:#E65100!important;}#mermaid-svg-129OZZhktrS5xKw7 .alloc tspan{fill:#E65100!important;}#mermaid-svg-129OZZhktrS5xKw7 .storage>*{fill:#F1F8E9!important;stroke:#7CB342!important;stroke-width:1.5px!important;color:#33691E!important;}#mermaid-svg-129OZZhktrS5xKw7 .storage span{fill:#F1F8E9!important;stroke:#7CB342!important;stroke-width:1.5px!important;color:#33691E!important;}#mermaid-svg-129OZZhktrS5xKw7 .storage tspan{fill:#33691E!important;}#mermaid-svg-129OZZhktrS5xKw7 .object>*{fill:#E8F5E9!important;stroke:#43A047!important;stroke-width:1.5px!important;color:#1B5E20!important;}#mermaid-svg-129OZZhktrS5xKw7 .object span{fill:#E8F5E9!important;stroke:#43A047!important;stroke-width:1.5px!important;color:#1B5E20!important;}#mermaid-svg-129OZZhktrS5xKw7 .object tspan{fill:#1B5E20!important;}#mermaid-svg-129OZZhktrS5xKw7 .lifetime>*{fill:#F3E5F5!important;stroke:#8E24AA!important;stroke-width:1.5px!important;color:#4A148C!important;}#mermaid-svg-129OZZhktrS5xKw7 .lifetime span{fill:#F3E5F5!important;stroke:#8E24AA!important;stroke-width:1.5px!important;color:#4A148C!important;}#mermaid-svg-129OZZhktrS5xKw7 .lifetime tspan{fill:#4A148C!important;}#mermaid-svg-129OZZhktrS5xKw7 .owner>*{fill:#EDE7F6!important;stroke:#5E35B1!important;stroke-width:1.5px!important;color:#311B92!important;}#mermaid-svg-129OZZhktrS5xKw7 .owner span{fill:#EDE7F6!important;stroke:#5E35B1!important;stroke-width:1.5px!important;color:#311B92!important;}#mermaid-svg-129OZZhktrS5xKw7 .owner tspan{fill:#311B92!important;}#mermaid-svg-129OZZhktrS5xKw7 .ptr>*{fill:#FFF8E1!important;stroke:#FFB300!important;stroke-width:1.5px!important;color:#E65100!important;}#mermaid-svg-129OZZhktrS5xKw7 .ptr span{fill:#FFF8E1!important;stroke:#FFB300!important;stroke-width:1.5px!important;color:#E65100!important;}#mermaid-svg-129OZZhktrS5xKw7 .ptr tspan{fill:#E65100!important;}#mermaid-svg-129OZZhktrS5xKw7 .destroy>*{fill:#FFEBEE!important;stroke:#E53935!important;stroke-width:1.5px!important;color:#B71C1C!important;}#mermaid-svg-129OZZhktrS5xKw7 .destroy span{fill:#FFEBEE!important;stroke:#E53935!important;stroke-width:1.5px!important;color:#B71C1C!important;}#mermaid-svg-129OZZhktrS5xKw7 .destroy tspan{fill:#B71C1C!important;}#mermaid-svg-129OZZhktrS5xKw7 .dealloc>*{fill:#FCE4EC!important;stroke:#D81B60!important;stroke-width:1.5px!important;color:#880E4F!important;}#mermaid-svg-129OZZhktrS5xKw7 .dealloc span{fill:#FCE4EC!important;stroke:#D81B60!important;stroke-width:1.5px!important;color:#880E4F!important;}#mermaid-svg-129OZZhktrS5xKw7 .dealloc tspan{fill:#880E4F!important;} Operating System
Virtual Memory / Physical Memory
malloc / operator new / allocator
Raw Storage
Object Construction
Object Lifetime Begins
Ownership / RAII
unique_ptr
shared_ptr
weak_ptr
Object Destruction
Object Lifetime Ends
Storage Deallocation
Allocator / OS