std::shared_ptr的别名构造函数
先看代码:
#include <iostream> #include <memory> struct Bar { int i{123}; }; struct Foo { Bar bar; }; int main(void) { std::shared_ptr<Bar> b; { auto f = std::make_shared<Foo>(); b = std::shared_ptr<Bar>(f, &(f->bar)); } std::cout << b->i << std::endl; return 0; }运行这段代码会怎样?
答案是:正确运行输出 123 。
接下来我们分析下:
代码核心逻辑
std::shared_ptr<Bar> b; { auto f = std::make_shared<Foo>(); b = std::shared_ptr<Bar>(f, &(f->bar)); // 别名构造 } std::cout << b->i << std::endl; // 输出 123关键点:shared_ptr别名构造函数(Aliasing Constructor)
template< class Y > shared_ptr( const shared_ptr<Y>& r, element_type* ptr ) noexcept;这个构造函数有两个相互独立的语义:
| 维度 | 说明 |
|---|---|
所有权 (control block) | 与 |
存储指针 (stored pointer) | 是用户传入的 |
也就是说:"管理什么" 和 "指向什么" 是解耦的。
执行时序分析
| 步骤 | Foo 引用计数 | 关键事件 |
|---|---|---|
| 1 | 在堆上构造 Foo(含 |
| 2 |
|
| 1 | Foo 不销毁( |
| 1 | 通过 |
| 0 | 调用原始 Foo 的 deleter,正确销毁整个 Foo(不是只销毁 Bar) |
总结要点
- Aliasing constructor 的本质:让一个
shared_ptr"搭便车"延长另一个对象的生命周期,同时对外暴露不同的指针视图。 - deleter 永远来自原始 control block:即使最后释放的是 aliasing
shared_ptr,它也调用原始对象的 deleter,不会用 aliasing 指针去delete。这就避免了对成员指针delete导致的 UB。 get()返回的是 stored pointer:题目解释中"calling get() on this shared_ptr will always return a copy of ptr"指的就是这个语义——返回&f->bar,而不是原始的 Foo 指针。- 典型用法:
- 指向成员(本题场景)
- 向下转型/侧向转型后保留所有权
- 让
shared_ptr<Derived>与shared_ptr<Base>共享 control block
- 使用约束:程序员需保证 stored pointer 在该
shared_ptr生命周期内一直有效(本例中由"成员位于被管理对象内部"自然保证)。
