0
点赞
收藏
分享

微信扫一扫

聊聊 C++ 中的几种智能指针 (下)

一:背景

上一篇我们聊到了C++ 的 ​​auto_ptr​​​ ,有朋友说已经在 ​​C++ 17​​​ 中被弃用了,感谢朋友提醒,今天我们来聊一下 ​​C++ 11​​ 中引入的几个智能指针。

  1. unique_ptr
  2. shared_ptr
  3. weak_ptr

看看它们都怎么玩。

二:三大智能指针详解

1. unique_ptr

上一篇在说 ​​auto_ptr​​​ 的时候聊过一个术语叫 ​​控制权转移​​,言外之意就是下面的代码会 访问违例 。

void test() {
int* i = new int(10);

auto_ptr<int> ptr1(i);
auto_ptr<int> ptr2 = ptr1; //将 ptr1 的控制台送给了 ptr2

cout << *ptr1 << endl;
}

int main() {
test();
}

这段代码用了赋值运算符,可能是程序员的无心之举,却引来程序的崩溃,那能不能规避下这种 ​​无心​​​ 的行为呢? 当然是可以的,屏蔽掉 ​​auto_ptr​​​ 中的赋值运算符不就可以了哈,C++ 中的 ​​unique_ptr​​ 就是这么实现的,可以看下源码。

class unique_ptr {

public:
unique_ptr(const unique_ptr&) = delete;
unique_ptr& operator=(const unique_ptr&) = delete;
};

可以看到,unique_ptr 将 ​​=,复制构造函数​​ 都给屏蔽了,所以再硬写的话,必然是报错。

聊聊 C++ 中的几种智能指针 (下)_#include

2. shared_ptr

这个算是纯正的 ​​引用计数​​​,高级语言中的 ​​引用跟踪​​ 经常会拿它做比较,说它存在循环引用等各种问题。

在循环引用之前,先说下它的简单用法。

void test() {
int* i = new int(10);
shared_ptr<int> ptr1(i);
shared_ptr<int> ptr2 = ptr1;
shared_ptr<int> ptr3 = ptr2;

printf("ptr.refcnt= %d, ptr2.refcnt=%d, ptr3.refcnt=%d \n",
ptr1.use_count(),
ptr2.use_count(),
ptr3.use_count());
}

int main() {
test();
}

聊聊 C++ 中的几种智能指针 (下)_c++_02

再看下源码,大概在内部共同维护了对 ​​_Ref_count_base​​ 引用。

class _Ref_count_base {
private:
_Atomic_counter_t _Uses = 1;
_Atomic_counter_t _Weaks = 1;
}

很显然当所有的析构函数将 ​​_Uses​​​ 降为 0 的时候就会对 ​​i​​ 进行释放,参考源码如下:

class shared_ptr : public _Ptr_base<_Ty> {
~shared_ptr() noexcept { // release resource
this->_Decref();
}
}

class _Ref_count_base {

void _Decref() noexcept { // decrement use count
if (_MT_DECR(_Uses) == 0) {
_Destroy();
_Decwref();
}
}
}

原理大概摸清楚了,接下来看下循环引用,它最大的问题就是让 ​​_Uses​​ 永远也不会为 0,

#include <iostream>
#include <string>

using namespace std;

class BClass;

class AClass
{
public:
int i = 10;
shared_ptr<BClass> b;
};


class BClass
{
public:
int i = 11;
shared_ptr<AClass> a;
};

void test() {

AClass* a = new AClass();
BClass* b = new BClass();

shared_ptr<AClass> aclass(a);
shared_ptr<BClass> bclass(b);

aclass->b = bclass;
bclass->a = aclass;

printf("a.refcnt=%d, b.refcnt=%d, a=%x,b=%x \n", aclass.use_count(), bclass.use_count(), &(*a), &(*b));
}

int main() {
test();
}

聊聊 C++ 中的几种智能指针 (下)_循环引用_03

接下来可以在 main 方法结束的地方观察 ​​a,b​​ 所有的 heap 块的内存内容是否被释放? 可以清楚的看到还在那里。。. 如下图所示:

聊聊 C++ 中的几种智能指针 (下)_c++_04

这就是 ​​循环引用​​​ 造成的问题,这时候就需要用到 ​​weak_ptr​​ 了。

3. weak_ptr

弱引用就是它的引用不改变对原对象中 ​​_Uses​​​ 的引用,接下来将 AClass 和 BClass 中的 ​​shared_ptr​​​ 改成 ​​weak_ptr​​ 版。

class AClass
{
public:
int i = 10;
weak_ptr<BClass> b;
};


class BClass
{
public:
int i = 11;
weak_ptr<AClass> a;
};

聊聊 C++ 中的几种智能指针 (下)_#include_05

从图中可以看到,此时 ​​refcnt=1​​​, 再观察下地址 ​​0x007f71a8​​ 的内容,可以发现已经被释放啦。

聊聊 C++ 中的几种智能指针 (下)_#include_06

好了,今天我们就聊这么多,希望对大家有帮助。



举报

相关推荐

0 条评论