unique_ptr和shared_ptr的引用计数机制有何不同?
好的,我们来详细解释一下 unique_ptr 和 shared_ptr 在引用计数机制上的不同:
核心区别
unique_ptr(唯一指针)- 没有引用计数机制。这是它的核心特征。
- 它代表了对所管理资源的独占所有权。这意味着在任何时刻,只有一个
unique_ptr实例可以指向该资源。 - 为了维护这种独占性,
unique_ptr禁止复制构造和复制赋值操作。如果你尝试将一个unique_ptr赋值给另一个或者用它初始化一个新的unique_ptr,编译器会报错。 - 所有权可以通过
std::move移动语义进行转移。移动操作会将所有权从一个unique_ptr转移到另一个unique_ptr,原始的unique_ptr会变为nullptr,不再拥有资源。移动操作不会涉及任何计数。 - 生命周期结束时释放资源:当拥有资源的
unique_ptr离开其作用域(被销毁)时,它所管理的资源会被立即释放(通过其关联的删除器)。 - 特点总结:轻量、高效、无额外开销、强制明确的资源所有权转移。
shared_ptr(共享指针)- 有引用计数机制。这是它实现共享所有权的核心。
- 它代表了对所管理资源的共享所有权。这意味着可以有多个
shared_ptr实例指向同一个资源。 - 引用计数原理:
- 当一个
shared_ptr被创建(例如通过构造函数或make_shared)并指向一个新资源时,会创建一个控制块(通常动态分配),其中包含一个引用计数器(初始值为 1)。 - 复制构造:当用一个
shared_ptr初始化另一个新的shared_ptr(即复制)时,它们指向同一个资源,并且引用计数器会递增 1。 - 赋值操作:当将一个
shared_ptr赋值给另一个shared_ptr时,被赋值的那个shared_ptr会先递减它原来指向资源的引用计数(如果原来有资源),然后指向新的资源,并递增新资源的引用计数。 - 析构/离开作用域:当一个
shared_ptr被销毁(例如离开作用域)时,它会递减它所指向资源的引用计数。 - 释放资源:只有当引用计数递减到 0 时,才意味着没有任何
shared_ptr拥有该资源了。此时,控制块会负责调用关联的删除器来释放资源本身,并最终释放控制块的内存。
- 当一个
- 特点总结:允许多个所有者、资源在所有共享指针都销毁后才释放、有额外的内存和性能开销(控制块和原子计数操作)、可能引入循环引用问题(需要配合
weak_ptr解决)。
总结对比表
| 特性 | unique_ptr | shared_ptr |
|---|---|---|
| 所有权模型 | 独占所有权 (Exclusive Ownership) | 共享所有权 (Shared Ownership) |
| 引用计数 | 无 | 有 |
| 复制语义 | 禁止复制 | 允许复制 (递增引用计数) |
| 移动语义 | 允许移动 (转移所有权) | 允许移动 (转移所有权) |
| 资源释放时机 | 唯一所有者销毁时立即释放 | 最后一个所有者销毁时释放 |
| 开销 | 极小 (接近原始指针) | 较大 (控制块、原子计数操作) |
| 主要用途 | 明确单一所有者的资源 | 需要多个所有者共享的资源 |
简单来说:unique_ptr 是“我的,只能是我的”,所以不需要计数;shared_ptr 是“大家的,谁最后走谁关灯”,所以需要计数来知道谁是最后一个。
