std::shared_ptr介绍

与旨在独占资源的 std::unique_ptr 不同,std::shared_ptr 专门解决多个智能指针共同拥有同一资源的需求。

这意味着可以有任意数量的 std::shared_ptr 指向同一对象。内部通过引用计数(reference count)来跟踪共享该资源的 std::shared_ptr 数量。只要至少还有一个 std::shared_ptr 指向该资源,资源就不会被释放;当最后一个 std::shared_ptr 离开作用域或被重新赋值时,资源才被析构。

std::unique_ptr 一样,std::shared_ptr 位于头文件 <memory>

示例:共享所有权

#include <iostream>
#include <memory> // for std::shared_ptr

class Resource
{
public:
    Resource()  { std::cout << "Resource acquired\n"; }
    ~Resource() { std::cout << "Resource destroyed\n"; }
};

int main()
{
    Resource* res{ new Resource };
    std::shared_ptr<Resource> ptr1{ res };

    {
        std::shared_ptr<Resource> ptr2{ ptr1 }; // 由 ptr1 拷贝构造
        std::cout << "Killing one shared pointer\n";
    } // ptr2 离开作用域,但资源仍在

    std::cout << "Killing another shared pointer\n";
    return 0;
} // ptr1 离开作用域,最后一个引用消失,资源被销毁

输出:

Resource acquired
Killing one shared pointer
Killing another shared pointer
Resource destroyed

重要区别:从裸指针独立创建多个 shared_ptr 的危险

下面示例看似相似,却会导致崩溃:

#include <iostream>
#include <memory>

class Resource { /* 同上 */ };

int main()
{
    Resource* res{ new Resource };
    std::shared_ptr<Resource> ptr1{ res };

    {
        std::shared_ptr<Resource> ptr2{ res }; // 直接从裸指针创建
        std::cout << "Killing one shared pointer\n";
    } // ptr2 离开作用域,误认为独占资源,提前释放

    std::cout << "Killing another shared pointer\n";
    return 0;
} // ptr1 再次释放同一资源 → 未定义行为(崩溃)

输出:

Resource acquired
Killing one shared pointer
Resource destroyed
Killing another shared pointer
Resource destroyed

根本原因:两个 std::shared_ptr 分别从同一裸指针独立创建,各自维护独立的控制块,引用计数互不知晓。
最佳实践:若需多个 std::shared_ptr 共享同一资源,必须通过拷贝现有 std::shared_ptr 来创建。


std::make_shared

std::make_unique 类似,std::make_shared(C++11 可用)推荐用于创建 std::shared_ptr

#include <iostream>
#include <memory>

class Resource { /* 同上 */ };

int main()
{
    auto ptr1{ std::make_shared<Resource>() };
    {
        auto ptr2{ ptr1 }; // 正确拷贝,共享引用计数
        std::cout << "Killing one shared pointer\n";
    }
    std::cout << "Killing another shared pointer\n";
}

使用 `std::make_shared` 的好处:

- 语法简洁,类型安全;  
- 避免独立创建多个 `shared_ptr` 导致的问题;  
- **性能优势**:一次性分配资源对象与控制块内存,减少二次分配开销。

---

## 深入实现细节

- `std::unique_ptr` 内部仅需单个指针;  
- `std::shared_ptr` 内部保存**两个指针**:  
  1. 指向托管对象;  
  2. 指向**控制块**(动态分配,保存引用计数等元数据)。  

若通过裸指针构造多个 `shared_ptr`,每个实例都会独立创建控制块 → 重复释放。  
通过拷贝/赋值现有 `shared_ptr`,共享同一控制块,引用计数正确递增。

---

## 从 unique_ptr 转换

```cpp
std::unique_ptr<Resource> up{ std::make_unique<Resource>() };
std::shared_ptr<Resource> sp{ std::move(up) }; // 合法,资源转移

反之,**不能**将 `shared_ptr` 转为 `unique_ptr`,因为共享语义违背独占语义。  
若函数返回智能指针,建议返回 `std::unique_ptr`,调用方按需转为 `std::shared_ptr` 即可。

---

## 注意事项

- 若任一 `std::shared_ptr` 未被正确销毁(例如所属对象泄漏),资源亦无法释放;  
- C++17 及更早版本,`std::shared_ptr` 不支持 C 风格数组;C++20 起支持数组特化。

---

## 结论

`std::shared_ptr` 适用于**多指针共享资源**场景。  
资源的生命周期由**最后一个引用**的 `std::shared_ptr` 控制,确保在适当时机自动、安全地释放。

关注公众号,回复"cpp-tutorial"

可领取价值199元的C++学习资料

公众号二维码

扫描上方二维码或搜索"cpp-tutorial"