仅调用shared_ptr指向对象的方法时,是否会实例化shared_ptr对象?
用户提问对应的代码示例如下:
class Foo{ int run(); }; class Bar{ // 注:返回值为shared_ptr,而非const引用 shared_ptr<Foo> GetFoo() const; }; Bar bar{...}; int k = bar.GetFoo()->run();
1. 调用GetFoo()时是否会实例化新的shared_ptr<Foo>对象?
默认会实例化新的shared_ptr<Foo>对象,但不会创建新的原子计数器等控制块结构:
shared_ptr的控制块(包含原子引用计数、删除器、分配器信息等)仅在首次通过裸指针构造shared_ptr、或者调用make_shared/allocate_shared创建对象时才会生成,后续所有拷贝、移动shared_ptr的操作都只会复用同一个控制块。- 按值返回
shared_ptr时,要么触发拷贝构造(返回类内部持有的shared_ptr成员),此时仅会对控制块的引用计数做原子加1操作;要么触发返回值优化(RVO/NRVO)、移动构造,此时连拷贝开销都可以省去,更不会生成新的控制块。
2. 编译器是否可以对最后一条语句做简化优化?
在满足条件的前提下可以做优化,甚至可以完全消除shared_ptr的全部开销:
- 最后一条语句中
GetFoo()返回的是临时shared_ptr对象,调用完run()之后就会立即销毁,对应的「引用计数加1、引用计数减1」是配对操作,只要编译器能确认这两个操作之间没有其他逻辑访问该控制块的引用计数,就可以根据as-if规则消除这对原子操作的开销,避免无意义的原子指令执行。 - 如果编译器能获取
GetFoo的实现逻辑,且确认Bar内部持有的Foo对象生命周期完全覆盖run()调用的周期,甚至可以直接优化为直接访问内部Foo对象调用run(),完全抹除shared_ptr的相关操作。 - 仅当
GetFoo的实现不在当前编译单元、且编译器没有开启跨编译单元优化时,才会执行完整的shared_ptr临时对象构造、引用计数增减、析构流程。
内容的提问来源于stack exchange,提问作者n1r44
相关产品推荐
相关产品推荐

