3 - shared_ptr
本文将对共享型智能指针share_ptr
做一些详细的介绍。
介绍
有时,多个对象或代码段需要同一指针的副本。unique_ptr
不能复制,不能用于此类情形。而std::shared_ptr
是一个可复制的支持共享所有权的智能指针,可以用于此类情形。
但是如果有多个shared_ptr
实例引用统一资源,它们如何知道何时实际释放资源?这就要说说shared_ptr
的工作原理了。
工作原理
在动态分配内存时,堆上的内存必须通过栈上的内存来寻址。也就是说栈上的指针是寻找堆内存的唯一方式。所以可以给堆内存添加一个 引用计数(Reference Counting),有几个指针指向它,它的引用计数就是几。当引用记录归零时,这块堆内存便会被释放。
使用
初始化
跟unique_ptr
必须用make_unique
初始化一样,shared_ptr
也必须用make_shared
来初始化。
// 使用make_shared初始化
std::shared_ptr<int> shared1 = std::make_shared<int>(3);
// 构造一个副本
std::shared_ptr<int> shared2(shared1);
注意:千万不要用裸指针去初始化shared_ptr
,容易出现内存泄漏的问题。
int* pi = new int(10);
std::shared_ptr<int> shared1(pi);
// 不小心delete
delete pi;
这样会导致堆内存重复释放两次,发生错误。
引用计数
可以使用use_count()
函数来查看shared_ptr
对象的引用次数。
std::cout << shared1.use_count() << std::endl;
自定义deleter
相较于unique_ptr
,shared_ptr
自定义deleter会更容易:
void close(FILE* filePtr)
{
if (filePtr == nullptr)
return ;
fclose(filePtr);
cout << "File Closed" << endl;
}
int main()
{
FILE* f = fopen("data.txt", "w");
// 将close()作为自定义的deleter
shared_ptr<FILE> filePtr(f, close);
// ......
}
为了演示share_ptr
的自定义deleter才用了旧的C风格文件读写,这个deleter帮我们完成了文件资源的关闭操作。
创建C风格数组
如下:
std::shared_ptr<int[]> shared1 = std::make_shared<int[]>(10);
std::cout << shared1[2] << std::endl;
常用函数
unique()
该函数判断shared_ptr
对象是否被独占,返回true/false
。
该函数已在C++17中被废弃,在C++20中被移除。原因是其实现方法并不能实现所代表的语义(多线程缺陷)
reset()
和unique_ptr
类似:
- 当reset函数有参数时,改变此
shared_ptr
对象指向的内存 - 当reset函数无参数时,将此
shared_ptr
对象置空。
如果这是最后的shared_ptr
(即独占某块内存),才会释放这块内存。
get()
同unique_ptr
,但不推荐使用,因为操作底层指针可能会带来内存泄漏问题。
swap()
首先是shared_ptr
自身提供的:
std::shared_ptr<int> shared1 = std::make_shared<int>(3);
std::shared_ptr<int> shared2 = std::make_shared<int>(5);
shared1.swap(shared2);
// 此时shared1指向5
std::cout << *shared1 << std::endl;
然后是标准库提供的:
std::swap(shared1, shared2);
推荐用自带的,因为有一些优化。
强制转换shared_ptr
正如存储某种类型的指针可以转换为存储另一种类型的指针一样,shared_ptr
也能进行类似的转换:
强制转换一般指针 | 强制转换shared_ptr |
---|---|
static_cast() | static_pointer_cast() |
dynamic_cast() | dynamic_pointer_cast() |
const_cast() | const_pointer_cast() |
reinterpret_cast() | reinterpret_pointer_cast() |
参考资料
- 飘零的落花 - 现代C++详解
- C++20高级编程
- C++20 shared_ptr为什么移除成员函数unique() (zhihu.com)