26.unique_ptr和shared_ptr区别

🔬 unique_ptr和shared_ptr区别

📖 内容概览

unique_ptr和shared_ptr是C++11引入的两种智能指针,它们都用于自动管理动态内存,避免内存泄漏。但它们在所有权管理、引用计数、使用场景等方面存在显著差异。本文将详细解析这两种智能指针的核心区别、使用方法、技术原理和最佳实践。

🎯 核心概念

🧩 unique_ptr核心特性

unique_ptr是一种独占所有权的智能指针:

  • 独占性:一个对象只能被一个unique_ptr管理
  • 无引用计数:不维护引用计数,性能更高
  • 移动语义:支持所有权转移,不支持拷贝
  • 自动释放:离开作用域时自动调用delete
  • RAII机制:资源获取即初始化

🧩 shared_ptr核心特性

shared_ptr是一种共享所有权的智能指针:

  • 共享性:一个对象可以被多个shared_ptr管理
  • 引用计数:维护引用计数,当计数为0时释放资源
  • 拷贝语义:支持拷贝和赋值
  • 线程安全:引用计数的增减是线程安全的
  • 自动释放:当最后一个shared_ptr离开作用域时释放资源

🔄 引用计数机制

shared_ptr的引用计数机制:

  • 增加引用计数:拷贝shared_ptr、赋值shared_ptr
  • 减少引用计数:shared_ptr离开作用域、调用reset()、赋值新值
  • 释放资源:当引用计数减少到0时,调用delete释放资源

🛠️ 代码示例

📝 基本使用对比

#include <iostream>
#include <memory>
#include <string>
using namespace std;
void testBasicUsage() {
cout << "\n=== 基本使用对比 ===" << endl;
// shared_ptr的基本使用
{
shared_ptr<string> sp1 = make_shared<string>("shared_ptr test");
shared_ptr<string> sp2 = sp1; // 拷贝,引用计数+1
shared_ptr<string> sp3(sp1); // 拷贝构造,引用计数+1
cout << "sp1: " << *sp1 << endl;
cout << "sp2: " << *sp2 << endl;
cout << "sp3: " << *sp3 << endl;
cout << "shared_ptr引用计数: " << sp1.use_count() << endl; // 输出:3
}
// unique_ptr的基本使用
{
unique_ptr<string> up1 = make_unique<string>("unique_ptr test");
// unique_ptr<string> up2 = up1; // 错误:不支持拷贝
unique_ptr<string> up2 = move(up1); // 正确:支持移动语义
cout << "up2: " << *up2 << endl;
// cout << "up1: " << *up1 << endl; // 错误:up1已释放所有权
}
}
int main() {
testBasicUsage();
return 0;

2. 所有权转移

class Test {
private:
int id_;
public:
Test(int id) : id_(id) {
cout << "🏗️ Test构造:id=" << id_ << endl;
}
~Test() {
cout << "💥 Test析构:id=" << id_ << endl;
}
void show() {
cout << "🔍 Test::show() called, id=" << id_ << endl;
}
};
void testOwnershipTransfer() {
cout << "\n=== 所有权转移 ===" << endl;
// unique_ptr的所有权转移
{
unique_ptr<Test> up1(new Test(1));
cout << "up1拥有所有权" << endl;
// 使用release()转移所有权
Test* raw_ptr = up1.release();
cout << "up1释放所有权,raw_ptr管理" << endl;
// 使用reset()转移所有权
unique_ptr<Test> up2;
up2.reset(raw_ptr);
cout << "up2获得所有权" << endl;
// 使用移动语义转移所有权
unique_ptr<Test> up3 = move(up2);
cout << "up3获得所有权" << endl;
}
// shared_ptr的所有权共享
{
shared_ptr<Test> sp1 = make_shared<Test>(2);
cout << "sp1引用计数: " << sp1.use_count() << endl;
{
shared_ptr<Test> sp2 = sp1;
cout << "sp2引用计数: " << sp2.use_count() << endl;
{
shared_ptr<Test> sp3(sp1);
cout << "sp3引用计数: " << sp3.use_count() << endl;
} // sp3离开作用域,引用计数-1
} // sp2离开作用域,引用计数-1
} // sp1离开作用域,引用计数-1,变为0,释放资源
}
int main() {
testOwnershipTransfer();
return 0;
}

3. 工厂模式示例

#include <iostream>
#include <memory>
#include <string>
using namespace std;
class Product {
private:
string name_;
public:
Product(const string& name) : name_(name) {
cout << "🏗️ Product构造:" << name_ << endl;
}
virtual ~Product() {
cout << "💥 Product析构:" << name_ << endl;
}
virtual void use() = 0;
};
class ConcreteProductA : public Product {
public:
ConcreteProductA() : Product("ConcreteProductA") {}
void use() override {
cout << "🔧 ConcreteProductA::use()" << endl;
}
};
class ConcreteProductB : public Product {
public:
ConcreteProductB() : Product("ConcreteProductB") {}
void use() override {
cout << "🔧 ConcreteProductB::use()" << endl;
}
};
// 使用unique_ptr的工厂模式
unique_ptr<Product> createProductUnique(const string& type) {
if (type == "A") {
return make_unique<ConcreteProductA>();
} else if (type == "B") {
return make_unique<ConcreteProductB>();
}
return nullptr;
}
// 使用shared_ptr的工厂模式
shared_ptr<Product> createProductShared(const string& type) {
if (type == "A") {
return make_shared<ConcreteProductA>();
} else if (type == "B") {
return make_shared<ConcreteProductB>();
}
return nullptr;
}
void testFactoryPattern() {
cout << "\n=== 工厂模式示例 ===" << endl;
// 使用unique_ptr的工厂
{
auto product1 = createProductUnique("A");
if (product1) {
product1->use();
}
auto product2 = createProductUnique("B");
if (product2) {
product2->use();
}
}
// 使用shared_ptr的工厂
{
auto product1 = createProductShared("A");
auto product2 = product1; // 共享所有权
cout << "shared_ptr引用计数: " << product1.use_count() << endl;
}
}
int main() {
testFactoryPattern();
return 0;
}

� 详细对比

1. 核心区别对比

特性unique_ptrshared_ptr
所有权独占所有权共享所有权
引用计数
性能更高(无引用计数开销)较低(引用计数增减开销)
拷贝操作禁止拷贝,支持移动支持拷贝和赋值
线程安全不保证线程安全引用计数增减是线程安全的
内存开销较小(仅存储指针)较大(存储指针+引用计数指针)
API复杂度简单复杂(支持更多操作)
循环引用不会产生可能产生,需要weak_ptr解决
数组支持原生支持(unique_ptr<T[]>)需要自定义删除器

2. 操作对比

| 操作 | unique_ptr | shared_ptr | | 构造函数 | 支持默认构造、带参构造、移动构造 | 支持默认构造、带参构造、拷贝构造、移动构造 | | 拷贝赋值 | 禁止 | 支持 | | 移动赋值 | 支持 | 支持 | | reset() | 支持 | 支持 | | release() | 支持(释放所有权) | 不支持 | | swap() | 支持 | 支持 | | get() | 支持(获取原始指针) | 支持(获取原始指针) | | use_count() | 不支持 | 支持(获取引用计数) | | unique() | 不支持 | 支持(检查是否独占) | | operator bool() | 支持(检查是否为空) | 支持(检查是否为空) | | operator-> | 支持 | 支持 | | operator* | 支持 | 支持 |

3. 适用场景对比

场景推荐使用不推荐使用
独占资源unique_ptrshared_ptr
共享资源-shared_ptr
性能敏感场景unique_ptrshared_ptr
容器元素unique_ptrshared_ptr(除非需要共享)
工厂模式返回值unique_ptrshared_ptr(除非需要共享)
多线程环境-shared_ptr(引用计数线程安全)
循环数据结构-shared_ptr + weak_ptr
数组管理unique_ptr<T[]>shared_ptr

⚠️ 注意事项

1. unique_ptr注意事项

  • 禁止拷贝:不要尝试拷贝unique_ptr,会导致编译错误
  • 移动后状态:移动后的unique_ptr处于有效但未定义的状态,不应再使用
  • 数组支持:使用unique_ptr<T[]>管理数组,自动调用delete[]
  • 自定义删除器:可以指定自定义删除器,用于管理特殊资源
  • release()的使用:release()返回的原始指针需要手动管理,或传递给另一个智能指针

2. shared_ptr注意事项

  • 循环引用:两个或多个shared_ptr互相引用会导致内存泄漏,需要使用weak_ptr解决
  • 性能开销:引用计数的增减会带来性能开销,频繁拷贝时需注意
  • 线程安全:shared_ptr本身不是线程安全的,只有引用计数的增减是线程安全的
  • make_shared的使用:优先使用make_shared创建shared_ptr,更高效
  • 避免裸指针:不要将同一个裸指针赋值给多个shared_ptr,会导致双重释放

3. 共同注意事项

  • 不要手动释放资源:智能指针会自动释放,手动释放会导致双重释放
  • 避免悬空指针:不要保存智能指针管理的原始指针,可能导致悬空指针
  • 注意作用域:智能指针离开作用域时会自动释放资源
  • 不要与裸指针混用:尽量避免将智能指针与裸指针混用,容易导致错误
  • 注意异常安全:智能指针能保证异常情况下资源的正确释放

📋 总结

1. 选择建议

  • 优先使用unique_ptr:当资源只需独占所有权时,unique_ptr是最佳选择,性能更高
  • 使用shared_ptr:当资源需要共享所有权时,使用shared_ptr
  • 避免不必要的共享:共享会带来引用计数开销,尽量使用unique_ptr
  • 使用make_unique和make_shared:更安全、更高效地创建智能指针

2. 最佳实践

// 推荐使用make_unique和make_shared创建智能指针
auto up = make_unique<MyClass>();
auto sp = make_shared<MyClass>();
// 禁止将同一个裸指针赋值给多个shared_ptr
MyClass* raw = new MyClass();
shared_ptr<MyClass> sp1(raw);
// shared_ptr<MyClass> sp2(raw); // 错误:会导致双重释放
// 避免循环引用
class A {
public:
shared_ptr<B> b_ptr;
};
class B {
shared_ptr<A> a_ptr;
// 改为使用weak_ptr
weak_ptr<A> a_ptr; // 使用weak_ptr避免循环引用
// 使用unique_ptr管理数组
unique_ptr<int[]> arr(new int[10]);
arr[0] = 42;
arr[1] = 100;

3. 核心要点回顾

  • unique_ptr:独占所有权,无引用计数,性能高,支持移动语义
  • shared_ptr:共享所有权,有引用计数,性能较低,支持拷贝语义
  • 选择依据:根据资源所有权需求选择合适的智能指针
  • 避免常见错误:循环引用、裸指针混用、手动释放资源
  • 优先使用工厂函数:make_unique和make_shared更安全高效 通过合理选择和使用unique_ptr和shared_ptr,可以实现安全、高效的内存管理,避免内存泄漏和其他内存相关问题。在实际开发中,应根据具体场景和需求选择合适的智能指针,遵循最佳实践,编写高质量的C++代码。

Thanks for reading!

26.unique_ptr和shared_ptr区别

2026-01-23
2269 字 · 11 分钟

已复制链接

评论区

目录