shared_ptr vs unique_ptr 的区别和用途(C++)

核心区别

特性unique_ptrshared_ptr
所有权独占所有权共享所有权
引用计数有(线程安全)
拷贝❌ 禁止拷贝✅ 可以拷贝
移动✅ 支持移动✅ 支持移动
性能开销极低(近乎原始指针)较高(引用计数开销)
内存占用和原始指针一样大是原始指针的2倍
适用场景明确单一所有权多个对象共享资源

1. unique_ptr - 独占所有权

特点

  • 一个对象只能被一个 unique_ptr 拥有

  • 不能拷贝,只能移动(std::move

  • 析构时自动删除对象

  • 零开销(和原始指针一样高效)

使用场景

  • 工厂模式返回对象

  • 容器中管理对象

  • PIMPL 模式

  • 任何明确只有一个所有者的情况

示例

cpp

#include <memory>
#include <iostream>
using namespace std;

class Resource {
public:
    Resource() { cout << "Resource created" << endl; }
    ~Resource() { cout << "Resource destroyed" << endl; }
    void use() { cout << "Resource used" << endl; }
};

// ✅ 正确用法
void test_unique_ptr() {
    // 创建 unique_ptr
    auto ptr1 = make_unique<Resource>();
    ptr1->use();
    
    // ❌ 不能拷贝
    // auto ptr2 = ptr1;  // 编译错误!
    
    // ✅ 可以移动(所有权转移)
    auto ptr2 = move(ptr1);
    ptr2->use();
    // ptr1 现在为空
    
    // 函数结束时自动释放
}

// 工厂模式返回 unique_ptr
unique_ptr<Resource> createResource() {
    return make_unique<Resource>();  // 移动语义,高效
}

// 在容器中使用
void test_container() {
    vector<unique_ptr<Resource>> vec;
    vec.push_back(make_unique<Resource>());
    vec.push_back(make_unique<Resource>());
    
    // 遍历(需要引用)
    for (auto& ptr : vec) {
        ptr->use();
    }
}

2. shared_ptr - 共享所有权

特点

  • 多个 shared_ptr 可以共享同一个对象

  • 引用计数:记录有多少个 shared_ptr 指向对象

  • 引用计数为 0 时自动删除对象

  • 可以拷贝(引用计数增加)

  • 有性能开销(维护引用计数)

使用场景

  • 多个对象需要共享同一资源

  • 观察者模式

  • 缓存系统

  • 复杂对象图

示例

cpp

#include <memory>
#include <iostream>
using namespace std;

class Resource {
public:
    Resource() { cout << "Resource created" << endl; }
    ~Resource() { cout << "Resource destroyed" << endl; }
    void use() { cout << "Resource used" << endl; }
};

void test_shared_ptr() {
    // 创建 shared_ptr
    auto ptr1 = make_shared<Resource>();
    cout << "引用计数: " << ptr1.use_count() << endl;  // 1
    
    // ✅ 可以拷贝(引用计数增加)
    auto ptr2 = ptr1;
    cout << "引用计数: " << ptr1.use_count() << endl;  // 2
    
    auto ptr3 = ptr1;
    cout << "引用计数: " << ptr1.use_count() << endl;  // 3
    
    ptr2->use();
    ptr3->use();
    
    // ptr2 析构,引用计数减为 2
    // ptr3 析构,引用计数减为 1
    // ptr1 析构,引用计数减为 0 → 删除对象
}

// 观察者模式中使用
class Subject;
class Observer {
    shared_ptr<Subject> _subject;  // 共享主题
};

void test_shared_ptr_container() {
    vector<shared_ptr<Resource>> vec;
    vec.push_back(make_shared<Resource>());
    vec.push_back(make_shared<Resource>());
    
    // 可以拷贝
    auto copy = vec[0];
    cout << "引用计数: " << vec[0].use_count() << endl;  // 2
}

3. weak_ptr - 配合 shared_ptr 使用

特点

  • 不增加引用计数

  • 解决循环引用问题

  • 需要 lock() 获取 shared_ptr

  • 可以检查对象是否还存在

示例

cpp

#include <memory>
#include <iostream>
using namespace std;

class Resource {
public:
    Resource() { cout << "Resource created" << endl; }
    ~Resource() { cout << "Resource destroyed" << endl; }
    void use() { cout << "Resource used" << endl; }
};

void test_weak_ptr() {
    auto shared = make_shared<Resource>();
    weak_ptr<Resource> weak = shared;  // 不增加引用计数
    
    cout << "引用计数: " << shared.use_count() << endl;  // 1
    
    // 使用 lock() 获取 shared_ptr
    if (auto ptr = weak.lock()) {
        ptr->use();  // 安全使用
    }
    
    shared.reset();  // 释放资源
    
    // weak 已失效
    if (auto ptr = weak.lock()) {
        // 不会执行
    } else {
        cout << "资源已释放" << endl;
    }
}

4. 如何选择?

决策树

text

需要共享所有权吗?
├─ 是 → 使用 shared_ptr
│   └─ 需要解决循环引用? → 使用 weak_ptr
└─ 否 → 使用 unique_ptr
    └─ 需要传递所有权? → 使用 move()

具体场景

场景推荐原因
工厂模式返回对象unique_ptr明确唯一所有权
容器中的对象unique_ptr独占所有权
观察者模式shared_ptr + weak_ptr多个观察者共享主题
缓存系统shared_ptr多个消费者共享缓存
树形结构unique_ptr(子节点)父节点拥有子节点
图形对象图shared_ptr + weak_ptr避免循环引用
PIMPL 模式unique_ptr唯一所有权
多线程共享shared_ptr线程安全的引用计数

5. 完整对比示例

cpp

#include <memory>
#include <iostream>
#include <vector>
using namespace std;

class MyClass {
public:
    MyClass(int v) : _value(v) { 
        cout << "MyClass(" << _value << ") created" << endl; 
    }
    ~MyClass() { 
        cout << "MyClass(" << _value << ") destroyed" << endl; 
    }
    int getValue() const { return _value; }
private:
    int _value;
};

// unique_ptr 示例
void unique_ptr_example() {
    cout << "\n=== unique_ptr 示例 ===" << endl;
    
    auto u1 = make_unique<MyClass>(10);
    auto u2 = make_unique<MyClass>(20);
    
    // u1->getValue();  // 10
    
    // 移动所有权
    auto u3 = move(u1);
    // u1 现在为空
    cout << "u3 value: " << u3->getValue() << endl;  // 10
    
    // 容器中使用
    vector<unique_ptr<MyClass>> vec;
    vec.push_back(move(u2));
    vec.push_back(move(u3));
    // vec.push_back(u2);  // ❌ 编译错误,不能拷贝
    
    cout << "vector size: " << vec.size() << endl;
}

// shared_ptr 示例
void shared_ptr_example() {
    cout << "\n=== shared_ptr 示例 ===" << endl;
    
    auto s1 = make_shared<MyClass>(100);
    cout << "引用计数: " << s1.use_count() << endl;  // 1
    
    auto s2 = s1;  // 共享所有权
    cout << "引用计数: " << s1.use_count() << endl;  // 2
    
    auto s3 = s1;
    cout << "引用计数: " << s1.use_count() << endl;  // 3
    
    // 容器中使用(可以拷贝)
    vector<shared_ptr<MyClass>> vec;
    vec.push_back(s1);
    vec.push_back(s2);
    vec.push_back(s3);
    cout << "vector size: " << vec.size() << endl;
    
    // 所有 shared_ptr 都有效
    for (auto& ptr : vec) {
        cout << "value: " << ptr->getValue() << ", refcount: " << ptr.use_count() << endl;
    }
}

// 性能对比
void performance_comparison() {
    cout << "\n=== 性能对比 ===" << endl;
    cout << "sizeof(MyClass*) = " << sizeof(MyClass*) << " bytes" << endl;
    cout << "sizeof(unique_ptr<MyClass>) = " << sizeof(unique_ptr<MyClass>) << " bytes" << endl;
    cout << "sizeof(shared_ptr<MyClass>) = " << sizeof(shared_ptr<MyClass>) << " bytes" << endl;
}

int main() {
    unique_ptr_example();
    shared_ptr_example();
    performance_comparison();
    return 0;
}

总结

特性unique_ptrshared_ptr
用途独占所有权共享所有权
拷贝
移动
引用计数
性能最快较慢
内存占用最小较大
使用场景明确单一所有者多个所有者共享

黄金法则

  • 默认使用 unique_ptr

  • 只有需要共享时才用 shared_ptr

  • 遇到循环引用时用 weak_ptr 打破

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值