c++ 中的智能指针

shared_ptr

可共享的指针
shared_ptr中包含两个指针,一个指向具体实例对象,另一个指向控制块(contorl block),控制块中包含一个引用计数和其他一些数据。

  • shared_ptr结构

在这里插入图片描述

  • 两个shared_ptr指向同一个对象
    在这里插入图片描述
  • 初始化:
  1. 使用new pointer
std::shared_ptr<T> ptr(new T());
std::shared_ptr<T> ptr = std::make_shared<T>();
  1. 使用existing pointer
shared_ptr<T> ptr(already_existing_pointer);
shared_ptr<T> ptr = make_shared(already_existing_pointer);

example:

  std::shared_ptr<int> p1;
  std::shared_ptr<int> p2 (nullptr);
  std::shared_ptr<int> p3 (new int);
  std::shared_ptr<int> p4 (new int, std::default_delete<int>());
  std::shared_ptr<int> p5 (new int, [](int* p){delete p;}, std::allocator<int>());
  std::shared_ptr<int> p6 (p5);
  std::shared_ptr<int> p7 (std::move(p6));
  std::shared_ptr<int> p8 (std::unique_ptr<int>(new int));

  std::shared_ptr<int> foo = std::make_shared<int> (10); //same as: std::shared_ptr<int> foo (new int(10));
  auto baz = std::make_shared<std::pair<int,int>> (30,40);

shared_ptr相关的一些方法

MethodDescription
reset()Resets the std::shared_ptr to empty, releasing ownership of the managed object.
use_count()Returns the current reference count, indicating how many std::shared_ptr instances share ownership.
unique()Check if there is only one std::shared_ptr owning the object (reference count is 1).
get()Returns a raw pointer to the managed object. Be cautious when using this method.
swap(shr_ptr2)swaps the contents (ownership) of two std::shared_ptr instances.

Example:

#include <iostream>
#include <memory>
using namespace std;
class A
{
public:
	void show()
	{
		cout << "A::show()" << endl;
	}
};
void testSharedPtr()
{
	shared_ptr<A> p1 = make_shared<A>();
	cout << "p1 use_count: " << p1.use_count() << endl;
	cout << "p1 address: " << p1.get() << endl;
	cout << "p1 isUnique: " << p1.unique() << endl;
	shared_ptr<A> p2 = p1;
	cout << "====== execute p2 = p1 ======" << endl;
	cout << "p1 use_count: " << p1.use_count() << endl;
	cout << "p2 use_count: " << p2.use_count() << endl;
	cout << "p2 address: " << p2.get() << endl;
	cout << "p2 isUnique: " << p2.unique() << endl;
	p1.reset();
	cout << "====== execute p1.reset() ======" << endl;
	cout << "p1 use_count: " << p1.use_count() << endl;
	cout << "p2 use_count: " << p2.use_count() << endl;
	cout << "p1 address: " << p1.get() << endl;
	cout << "p2 address: " << p2.get() << endl;
	cout << "p1 isUnique: " << p1.unique() << endl;
	cout << "p2 isUnique: " << p2.unique() << endl;
	p1.swap(p2);
	cout << "====== execute p1.swap(p2) ======" << endl;
	cout << "p1 use_count: " << p1.use_count() << endl;
	cout << "p2 use_count: " << p2.use_count() << endl;
	cout << "p1 address: " << p1.get() << endl;
	cout << "p2 address: " << p2.get() << endl;
	cout << "p1 isUnique: " << p1.unique() << endl;
	cout << "p2 isUnique: " << p2.unique() << endl;
}

Result:

p1 use_count: 1
p1 address: 0000016C4EEC4BF0
p1 isUnique: 1
====== execute p2 = p1 ======
p1 use_count: 2
p2 use_count: 2
p2 address: 0000016C4EEC4BF0
p2 isUnique: 0
====== execute p1.reset() ======
p1 use_count: 0
p2 use_count: 1
p1 address: 0000000000000000
p2 address: 0000016C4EEC4BF0
p1 isUnique: 0
p2 isUnique: 1
====== execute p1.swap(p2) ======
p1 use_count: 1
p2 use_count: 0
p1 address: 0000016C4EEC4BF0
p2 address: 0000000000000000
p1 isUnique: 1
p2 isUnique: 0

unique_ptr

独占指针,只能转移所有权,不能拷贝赋值

void testUniquePtr()
{
	unique_ptr<A> p1(new A());
	cout << "p1 address: " << p1.get() << endl;
	unique_ptr<A> p2 = move(p1);
	cout << "====== execute \"p2 = move(p1)\" ======" << endl;
	cout << "p1 address: " << p1.get() << endl;
	cout << "p2 address: " << p2.get() << endl;
	//unique_ptr<A> p2 = p1; //compile error: can't copy unique_ptr
}

Result:

p1 address: 000001507EE7DCE0
====== execute "p2 = move(p1)" ======
p1 address: 0000000000000000
p2 address: 000001507EE7DCE0

weak_ptr

为了解决循环引用问题而引入的,weak_ptr不拥有其指向的对象,也就是说,让weak_ptr指向shared_ptr所指向对象,对象的引用计数并不会增加。

auto ptr = make_shared<string>("test");
weak_ptr<string> wp1{ ptr };
cout << "use count: " << ptr.use_count() << endl;// use count: 1

weak_ptr无法直接访问对象成员

struct Square {
    int size = 0;
};
auto sp = make_shared<Square>();
weak_ptr<Square> wp{ sp };
cout << wp->size << endl;   // compile-time ERROR
//通过shared_ptr访问对象成员
//weak_ptr不持有对象,也就是说weak_ptr指向的对象可能析构了,但weak_ptr却不知道。所以需要判断weak_ptr指向的对象是否还存在,有两种方式:
//1. weak_ptr的use_count()方法,判断引用计数是否为0。
//2. 调用weak_ptr的expired()方法,若对象已经被析构,则expired()将返回true。
auto sp = make_shared<Square>();
weak_ptr<Square> wp{ sp };
    
if (!wp.expired()) 
{
    auto ptr = wp.lock();      // get shared_ptr
    cout << ptr->size << endl;
}
循环引用导致内存泄露
#include "shared_ptr.h"
#include <iostream>
#include <memory>
using namespace std;
class Son;
class Father
{
public:
	Father()
	{
		cout << "Father construct" << endl;
	}
	virtual ~Father()
	{
		cout << "Father destruct" << endl;
	}
	shared_ptr<Son> son;
};

class Son
{
public:
	Son()
	{
		cout << "Son construct" << endl;
	}
	virtual ~Son()
	{
		cout << "Son destruct" << endl;
	}
	shared_ptr<Father> father;
};

void testWeakPtr()
{
	shared_ptr<Father> pFather = make_shared<Father>();
	cout << "pFather use_count: " << pFather.use_count() << endl;
	shared_ptr<Son> pSon = make_shared<Son>();
	cout << "pSon use_count: " << pSon.use_count() << endl;
	pFather->son = pSon;
	cout << "====== execute \"pFather->son = pSon\" ======" << endl;
	cout << "pFather use_count: " << pFather.use_count() << endl;
	cout << "pSon use_count: " << pSon.use_count() << endl;
	pSon->father = pFather;
	cout << "====== execute \"pSon->father = pFather\" ======" << endl;
	cout << "pFather use_count: " << pFather.use_count() << endl;
	cout << "pSon use_count: " << pSon.use_count() << endl;
	cout << "==========" << endl;
}

Result:

Father construct
pFather use_count: 1
Son construct
pSon use_count: 1
====== execute "pFather->son = pSon" ======
pFather use_count: 1
pSon use_count: 2
====== execute "pSon->father = pFather" ======
pFather use_count: 2
pSon use_count: 2
==========

分析:
main函数退出之前,Father和Son对象的引用计数都是2。
son指针销毁,这时Son对象的引用计数是1。
father指针销毁,这时Father对象的引用计数是1。
由于Father对象和Son对象的引用计数都是1,这两个对象都不会被销毁,从而发生内存泄露。

使用weak_ptr修复内存泄露
#include "shared_ptr.h"
#include <iostream>
#include <memory>
using namespace std;
class Son;
class Father
{
public:
	Father()
	{
		cout << "Father construct" << endl;
	}
	virtual ~Father()
	{
		cout << "Father destruct" << endl;
	}
	shared_ptr<Son> son;
};

class Son
{
public:
	Son()
	{
		cout << "Son construct" << endl;
	}
	virtual ~Son()
	{
		cout << "Son destruct" << endl;
	}
	weak_ptr<Father> father;
};

void testWeakPtr()
{
	shared_ptr<Father> pFather = make_shared<Father>();
	cout << "pFather use_count: " << pFather.use_count() << endl;
	shared_ptr<Son> pSon = make_shared<Son>();
	cout << "pSon use_count: " << pSon.use_count() << endl;
	pFather->son = pSon;
	cout << "====== execute \"pFather->son = pSon\" ======" << endl;
	cout << "pFather use_count: " << pFather.use_count() << endl;
	cout << "pSon use_count: " << pSon.use_count() << endl;
	pSon->father = pFather;
	cout << "====== execute \"pSon->father = pFather\" ======" << endl;
	cout << "pFather use_count: " << pFather.use_count() << endl;
	cout << "pSon use_count: " << pSon.use_count() << endl;
	cout << "==========" << endl;
}

Result:

Father construct
pFather use_count: 1
Son construct
pSon use_count: 1
====== execute "pFather->son = pSon" ======
pFather use_count: 1
pSon use_count: 2
====== execute "pSon->father = pFather" ======
pFather use_count: 1
pSon use_count: 2
==========
Father destruct
Son destruct

分析:
main函数退出前,Son对象的引用计数是2,而Father的引用计数是1。
son指针销毁,Son对象的引用计数变成1。
father指针销毁,Father对象的引用计数变成0,导致Father对象析构,Father对象的析构会导致它包含的son_指针被销毁,这时Son对象的引用计数变成0,所以Son对象也会被析构。

Reference:
https://www.geeksforgeeks.org/shared_ptr-in-cpp/
https://cplusplus.com/reference/memory/shared_ptr/shared_ptr/
https://www.geeksforgeeks.org/auto_ptr-unique_ptr-shared_ptr-weak_ptr-in-cpp/
谈谈 shared_ptr 的那些坑

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值