智能指针学习——共享指针

弱引用

前言

#include <iostream>
#include <string>
#include <memory>

using namespace std;

class Girl;

class Boy {
public:
	Boy() {
		cout << "Boy 构造函数" << endl;
	}

	~Boy() {
		cout << "~Boy 析构函数" << endl;
	}

	void setGirlFriend(shared_ptr<Girl> _girlFriend) {
		this->girlFriend = _girlFriend;
	}

private:
	shared_ptr<Girl> girlFriend;
};

class Girl {
public:
	Girl() {
		cout << "Girl 构造函数" << endl;
	}

	~Girl() {
		cout << "~Girl 析构函数" << endl;
	}

	void setBoyFriend(shared_ptr<Boy> _boyFriend) {
		this->boyFriend = _boyFriend;
	}

private:
	shared_ptr<Boy> boyFriend;
};


void useTrap() {
	shared_ptr<Boy> spBoy(new Boy());
	shared_ptr<Girl> spGirl(new Girl());

	// 陷阱用法
	spBoy->setGirlFriend(spGirl);
	spGirl->setBoyFriend(spBoy);
	// 此时boy和girl的引用计数都是2
}


int main(void) {
	useTrap();

	system("pause");
	return 0;
}

在这里插入图片描述
这里会发现没有调用Boy和Girl的析构函数,因为:
在这里插入图片描述
在这里插入图片描述
最后spBoy和spGirl的引用计数都会置为1,而shared_ptr和shared_ptr只有当引用计数为0时才会对所引用的指针进行析构

而对于这种情况可以释放掉内存:
在这里插入图片描述

首先释放spBoy,但是因为girl对象里面的智能指针还托管着boy,boy的引用计数为2,所以释放spBoy时,引用计数减1,boy的引用计数为1;
在释放spGirl,girl的引用计数减1,为零,开始释放girl的内存,因为girl里面还包含有托管boy的智能指针对象,所以也会进行boyFriend的内存释放,boy的引用计数减1,为零,接着开始释放boy的内存。最终所有的内存都释放了。

weak_ptr

概念:
weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。 同时weak_ptr 没有重载*和->但可以使用 lock 获得一个可用的 shared_ptr 对象。

弱指针的使用;
weak_ptr wpGirl_1; // 定义空的弱指针
weak_ptr wpGirl_2(spGirl); // 使用共享指针构造
wpGirl_1 = spGirl; // 允许共享指针赋值给弱指针
#include <iostream>
#include <string>
#include <memory>

using namespace std;

class Girl;

class Boy {
public:
	Boy() {
		cout << "Boy 构造函数" << endl;
	}

	~Boy() {
		cout << "~Boy 析构函数" << endl;
	}

	void setGirlFriend(shared_ptr<Girl> _girlFriend) {
		this->girlFriend = _girlFriend;


		// 在必要的使用可以转换成共享指针
		shared_ptr<Girl> sp_girl;
		sp_girl = this->girlFriend.lock();

		cout << sp_girl.use_count() << endl;
		// 使用完之后,再将共享指针置NULL即可
		sp_girl = NULL;
	}

private:
	weak_ptr<Girl> girlFriend;
};

class Girl {
public:
	Girl() {
		cout << "Girl 构造函数" << endl;
	}

	~Girl() {
		cout << "~Girl 析构函数" << endl;
	}

	void setBoyFriend(shared_ptr<Boy> _boyFriend) {
		this->boyFriend = _boyFriend;
	}

private:
	shared_ptr<Boy> boyFriend;
};


void useTrap() {
	shared_ptr<Boy> spBoy(new Boy());
	shared_ptr<Girl> spGirl(new Girl());

	spBoy->setGirlFriend(spGirl);
	spGirl->setBoyFriend(spBoy);
}


int main(void) {
	useTrap();

	system("pause");
	return 0;
}

这样就可以避免“死锁”的问题

使用陷阱

  1. 不要让一个裸指针初始化多个 shared_ptr ;

  2. 不要主动删除 shared_ptr 所管理的裸指针

BigObj *p = new BigObj();
std::shared_ptr<BigObj> sp(p);
std::shared_ptr<BigObj> sp1(p);
delete p;

这里让一个裸指针初始化两个 shared_ptr ,会导致该指针被删除两次,这是错误的。另外,既然我们让智能指针管理裸指针了,就不用再自己手动去删除改指针了,否则也会导致两次删除,应该让 shared_ptr 自动去删除所管理的裸指针。
(所以使用shared_ptr的赋值都是shared_ptrA=shatred_ptrB。在创建智能指针时候都是shared_ptr ptr(new xxx),或者shared_ptr ptr.reset(new xxx),不能new创建了对象p再用shared_ptr指向p这种分步骤的)

实例:https://blog.csdn.net/zhangruijerry/article/details/100927531

3.不要在函数实参中创建shared_ptr。
例如:

function(shared_ptr<int>(new int), g()); //有缺陷

首先先要了解new一个对象的过程:

  1. 调用某个 operator new 分配内存
  2. 在该地址上构造T的对象

再了解函数参数的计算顺序:
有可能是从左到右解析形参,也有可能从右到左

所以有可能出现:先new int,然后调用g(),如果恰好g()发生异常,而shared_ptr还没有创建, 则int内存泄漏了

共享指针shared_ptr提供的默认删除程序将调用delete,而不是delete []。 这意味着仅当共享指针拥有使用new创建的单个对象时,默认删除器才适用。

std::shared_ptr<char> ptr(new char[20]); //错误,但能编译通过

因此,如果使用new []创建对象数组,则必须自定义删除器。 你可以通过传递函数,函数对象或lambda来做到这一点,后者对传递的普通指针调用delete []。 例如:

std::shared_ptr<char> ptr(new char[20],
                       [](char* p) {
                           delete[] p;
                       }
                      );

对于共享指针初始化,括号内第二个部分为用户自己定义的删除器。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值