使用raw指针和make_shared创建shared_ptr对象,优先使用make_shared,unique_ptr也一样

本文探讨了在C++中使用std::make_shared而非直接通过原始指针创建std::shared_ptr对象的原因。阐述了make_shared在代码清晰度、效率提升、减少内存分配次数以及避免异常安全问题方面的优势。

先介绍不能使用make_shared的场景:
1.当你需要定制一个删除器,例如采用shared_ptr管理非内存资源或者一个分配在非标准内存区域的对象。这时候不能使用make_shard,它不支持指定删除器;
2.当你正在采用其他代码产生的raw指针(指向你需要处理的对象),直接采用raw指针构造share_ptr对象。
更适合使用make_shared的原因是:
1.用起来更清晰,准确;
2.效率比直接使用raw指针构造更高;
auto sp1 = shared_ptr{ new widget{} };
auto sp2 = sp1;
如下图所示,两次分开的内存分配花销。
使用raw指针
auto sp1 = make_shared();
auto sp2 = sp1;
如下图所示,make_shared只要一次简单的内存分配花销。降低了内存花销,包括内存碎片的产生,提高高速缓存性能。
使用make_shared
3.避免明确使用new;
4.避免异常安全问题。
void sink( unique_ptr< widget >, unique_ptr< gadget > );
sink( unique_ptr< widget >{new widget{}}, unique_ptr< gadget >{new gadget{}} ); // Q1: do you see the problem?
c++参数的执行顺序不确定,如果先执行new widget,当执行new gadget抛出异常,widget出现内存泄漏。
sink( make_unique< widget >(), unique_ptr< gadget >{new gadget{}} );
同样出现内存泄露 如果先执行new gadget,当执行make_unique< widget >出现异常的时候,gadget出现内存泄露。
sink( make_unique< widget >(), make_unique< gadget >() ); // exception-safe

<think>好的,用户的问题是关于C++中std::shared_ptr使用原始指针创建时是否会生成控制块,以及控制块的作用实现机制。首先我需要回忆一下C++智能指针的相关知识,尤其是shared_ptr的实现细节。 根据引用1的内容,当shared_ptr从原始指针构造时,会创建控制块。控制块的作用是管理对象的生命周期,包含引用计数等信息。用户提到的三个情况:从raw pointer构造、从unique_ptr构造、通过make_shared构造都会创建控制块。但需要注意的是,如果多次用同一个原始指针创建shared_ptr,会导致多个控制块,这会有问题,因为每个控制块独立计数,可能导致重复释放。这点可能需要提醒用户。 接下来,控制块的结构通常包括强引用计数(shared count)弱引用计数(weak count)。当强引用归零时,对象被销毁;弱引用归零时,控制块本身才会释放。实现上,控制块可能是一个独立的内存块,与管理的对象分开,或者当使用make_shared时,对象控制块可能分配在连续内存中,提高效率。 用户可能还关心如何避免多个控制块的情况。比如,应该避免直接用同一个原始指针多次创建shared_ptr,而应该一次创建后通过拷贝来共享所有权。或者使用enable_shared_from_this来安全地获取shared_ptr实例。 另外,引用3提到unique_ptr不能直接共享所有权,必须转为shared_ptr,这时候也会创建控制块。引用2中的例子展示了如何传递shared_ptr参数,但需要注意参数传递时的引用计数变化。 用户的问题还可能涉及控制块的线程安全,引用4提到原子操作锁的问题,但这里用户主要问的是创建时是否生成控制块,可能暂时不需要深入线程安全部分,但可以简要提及控制块中的引用计数是原子操作的。 总结回答的结构应该是:先明确回答使用raw pointer创建shared_ptr会生成控制块,然后解释控制块的作用(管理生命周期、存储引用计数等),接着说明实现机制,包括控制块的内存分配、引用计数的管理,最后给出注意事项,如避免多次从同一raw pointer创建shared_ptr导致多个控制块的问题,以及推荐使用make_shared来避免分离内存分配。 需要确保回答符合用户给出的格式要求,使用中文,行内公式用$...$,独立公式用$$...$$,并且正确引用提供的参考文献。可能还需要生成相关问题,比如如何避免控制块重复创建make_shared的优势,shared_ptr线程安全性等。</think>在使用原始指针创建 `std::shared_ptr` 时,**会分配控制块**。控制块是 `std::shared_ptr` 实现引用计数生命周期管理的核心机制。 --- ### **控制块的作用** 1. **引用计数管理** 控制块存储以下关键信息: - **强引用计数(shared count)**:表示当前有多少个 `shared_ptr` 共享对象所有权。 - **弱引用计数(weak count)**:表示当前有多少个 `std::weak_ptr` 观察对象。 - **删除器(deleter)**:定义对象销毁时的行为(例如 `delete` 或自定义删除函数)。 - **分配器(allocator)**(可选):用于控制块的内存分配策略[^1]。 当强引用计数归零时,对象会被销毁;当弱引用计数也归零时,控制块自身的内存才会释放。 2. **线程安全** 控制块中的引用计数通过原子操作实现线程安全的增减,确保多线程环境下引用计数的正确性[^4]。 --- ### **实现机制** - **控制块分配时机** 当通过 `new` 的原始指针构造 `std::shared_ptr` 时,控制块会与对象分离,单独分配内存。例如: ```cpp int* raw_ptr = new int(42); std::shared_ptr<int> sp1(raw_ptr); // 分配控制块 ``` 此时,控制块对象的内存地址不同(见下图)。 - **`make_shared` 的优化** 若通过 `std::make_shared` 创建 `shared_ptr`,对象控制块会分配在**同一块连续内存**中,减少内存分配开销并提高局部性。例如: ```cpp auto sp2 = std::make_shared<int>(42); // 对象控制块内存连续 ``` --- ### **关键注意事项** 1. **避免重复创建控制块** 若对同一个原始指针多次构造 `shared_ptr`,会导致多个控制块,引发重复释放问题: ```cpp int* raw_ptr = new int(42); std::shared_ptr<int> sp1(raw_ptr); std::shared_ptr<int> sp2(raw_ptr); // 错误!两个独立的控制块 ``` 此时 `sp1` `sp2` 会各自管理独立的控制块,导致对象被释放两次[^1]。 2. **使用 `enable_shared_from_this`** 若对象需要安全地生成 `shared_ptr`,需继承 `std::enable_shared_from_this`,通过 `shared_from_this()` 获取已关联控制块的 `shared_ptr`[^3]。 --- ### **总结** | 创建方式 | 控制块分配位置 | 内存效率 | 安全性 | |-----------------------|----------------------|----------|----------------| | `shared_ptr(raw_ptr)` | 对象与控制块分离 | 低 | 需避免重复使用 | | `make_shared` | 对象与控制块连续 | 高 | 安全 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值