C++语法学习笔记三十七:shared_ptr常用操作、计数、自定义删除器等等

实例代码:

// 

#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <memory>


using namespace std;

void myfunc(shared_ptr<int> ptmp){
	return;
}


shared_ptr<int> myfunc1(shared_ptr<int>& ptmp){ // 如果参数为引用,则智能指针的引用计数不会增加
	return ptmp;
}

void myDelete(int* p){ //我们的删除器,删除整形指针用的,当智能指针引用计数为0, 就会自动调用该删除器来删除对象。
	//写一些日志
	delete p; //既然你自己提供了删除器来取代智能指针的缺省删除器,你就有义务自己来删除所指向的对象。(引用计数为0时)
}

class A{
public:
	A(){};
	~A(){};
};

// 写个函数模板来封装shared_ptr数组
template<typename T>
shared_ptr<T> make_shared_array(size_t size){
	return shared_ptr<T>(new T[size], default_delete<T[]>());
}

int main()
{
	// 一、shared_ptr引用计数的增加和减少
	// 共享式,引用计数,每一个shared_ptr的拷贝都指向相同的内存(对象),只有最后一个指向该对象的shared_ptr指针不需要再指向该对象的时候,这个shared_ptr才会去析构所指向的对象。

	// (1.1) 引用计数的增加
	// 每个shared_ptr 都回记录有多少个其他的shared_ptr指向相同的对象;
	auto p6 = make_shared<int>(100); // 目前p6所指向的对象只有p6一个引用者;
	auto p7(p6); //智能指针定义时的初始化,p7和p6指向了相同的对象,此对象目前有两个引用者;

	//在如下情况下,所有指向这个对象的shared_ptr引用计数都会增加1
	// a)向上边这样,我们用p6来初始化p7这个智能指针;
	// b)把智能指针当做实参往函数里传递;
	myfunc(p7);
	// c)作为函数的返回值
	auto p8 = myfunc1(p7); // 引用计数会变成3,这是因为有p8来接这个临时的智能指针。
	myfunc(p7); // 没有变量来接这个临时的智能指针,则临时智能指针生命周期到了,引用计数从3恢复到2;

	// (1.2) 引用计数的减少
	//a). 给shared_ptr赋予新值,让该shared_ptr指向一个新对象;
	p8 = make_shared<int>(200); // p8 指向新对象,计数为1, p6, p7 指向的对象计数恢复为2;
	p7 = make_shared<int>(300); // p7 指向新对象,计数为1, p6 指向的对象计数恢复为1;
	p6 = make_shared<int>(400); // p6 指向新对象,计数为1, p6指向的原内存被释放;

	//b). 局部的shared_ptr离开其作用域;
	auto p9 = make_shared<int>(100); // 目前p6所指向的对象只有p6一个引用者;
	auto p10(p9); // p7 和 p6 指向相同的对象,引用计数为2;
	myfunc(p10);// 进入函数体myfunc中是3个引用计数,从myfunc中退出时引用计数恢复为2个;

	//c). 当一个shared_ptr引用计数从1变成0,则它会自动释放自己所管理
	auto p11 = make_shared<int>(100); // 只有p9指向该对象
	auto p12 = make_shared<int>(100); 
	// p9  = p10; // 给p9赋值会让p9指向p10所指向的对象,该对象引用计数变成2, 而原来p9所指向的对象引用计数会从1变成0,从而导致被释放。

	// 二、shared_ptr指针常用操作
	// (2.1) use_cout(): 返回多少个智能指针指向某个对象,主要用于调试目的;
	shared_ptr<int> myp(new int(100));
	int icout = myp.use_count(); // 1
	shared_ptr<int> myp2(myp);
	icout = myp.use_count(); // 2
	shared_ptr<int> myp3;
	myp3 = myp2;
	icout = myp3.use_count(); // 3
	icout = myp.use_count(); // 3


	// (2.2) unique() : 是否该智能指针独占某个指向的对象,也就是若只有一个智能指针指向某个对象,则unique()返回true,否则返回false;
	shared_ptr<int> myp4(new int(100));
	shared_ptr<int> myp5(myp4);
	if (myp4.unique()) { // 成立
		cout << "unique OK" << endl;
	}
	else{
		cout << "unique NO NO NO" << endl;
	}
	shared_ptr<int> myp6;
	if (myp6.unique()) { //成立
		cout << "unique OK " << endl;
	}

	// (2.3) reset() : 恢复(复位/重置)的意思
	// a). reset()不带参数时
	// 若pi 是唯一指向该对象的指针,那么释放pi所指向的对象,并将pi置空
	shared_ptr<int> pi(new int(100));
	pi.reset();
	if (pi == nullptr)
	{
		cout << "pi 被置空" << endl;
	}
	//若pi不是唯一指向该对象的指针,那么不释放pi所指向的对象,但指向该对象的引用计数会减少1,同时将pi置空
	shared_ptr<int> pi1(new int(100));
	auto pi2(pi1);
	pi1.reset();
	// b). reset()带参数(一般是一个new出来的指针)时
	// 若pi1是唯一指向该对象的指针,则释放pi1指向的对象,让pi1指向新对象。
	// 若pi1不是唯一指向该对象的指针,则不释放pi1指向的对象,但指向该对象的引用计数会减少1,同时让pi1指向新对象;
	shared_ptr<int> pi3(new int(100));
	auto pi4(pi3); // pi2引用计数会变成2
	pi3.reset(new int(1)); //释放原内存,指向新内存;
	if (pi3.unique()){
		cout << "pi3 unique OK" << endl;
	}

	if (pi4.unique()) {
		cout << "pi4 unique OK" << endl;
	}

	//空指针也可以通过reset来重新初始化
	shared_ptr<int> p;
	p.reset(new int(1)); //释放p所指向的对象,让p指向新对象,因为原来p为空的,所以就等于啥也没释放直接指向新对象;

	// (2.4) *解引用:获得p指向的对象
	shared_ptr<int> pother(new int(123456));
	cout << *pother << endl;

	// (2.5) get(): 考虑到有些函数(第三方函数)的参数需要的是一个内置裸指针而不是智能指针。
	// p.get() : 返回p中保存的指针(裸指针),小心使用,如果智能指针释放了所指向的对象,那么这个返回的裸指针也就变得无效了;

	shared_ptr<int> myp7(new int(100));
	int *p111 = myp7.get();
	*p111 = 45;

	delete p111; //千万不要这么干,不然系统会报告异常,产生不可预料的结果;

	// (2.6) swap():交换两个智能指针所指向的对象
	shared_ptr<string> ps1(new string("I Love China1!"));
	shared_ptr<string> ps2(new string("I Love China2!"));
	std::swap(ps1, ps2);
	ps1.swap(ps2);

	// (2.7) = nullptr
	// a).将所指向的对象引用计数减1,若引用计数变为0, 则释放智能指针所指向的对象。
	// b) .将智能指针置空
	shared_ptr<string> ps3(new string("I Love China!"));
	shared_ptr<string> ps4(ps3); //
	ps3 = nullptr;

	// (2.8) 智能指针名字作为判断条件
	shared_ptr<string> ps5(new string("I Love China!"));
	ps5 = nullptr;
	if (ps5){
		cout << "ps3指向一个对象" << endl;
	}
	else{
		cout << "ps1为空" << endl;
	}


	// (2.9) 指定删除器以及数组问题
	// a). 指定删除器;
	// 一定时机帮我们删除所指向的对象,delete : 将delete运算符号作为默认的资源析构方式。
	// 我们可以指定自己的删除器取代系统提供的默认删除器,当智能指针需要删除所指向的对象时,编译器就会调用我们自己提供的删除器来删除。

	//shared_ptr指定删除器方法比较简单,一般只需要在参数中添加具体的删除器函数名即可;
	shared_ptr<int> p13(new int(123456), myDelete);
	shared_ptr<int> p14(p13); // 两个引用计数
	p14.reset(); // 剩1个引用计数,p2为 nullptr
	p13.reset(); // 因为此时只有1个引用计数,所以要释放指向的对象,调用我们的删除器myDelete,同时p置空;


	//删除器可以是一个lambda表达式:
	shared_ptr<int> ps(new int(123456), [](int *p){
		delete p;
	});

	//有些情况,默认删除器处理不了,(用shared_ptr管理动态数组),需要我们提供自己指定的删除器;
	shared_ptr<int> p22(new int[10], [](int *p){
		delete[]p;
	});

	shared_ptr<A> pA33(new A[10]); // 异常因为系统释放pA是delete pA所指向的裸指针,而不delete []pA,这种情况下我们需要写自己的删除器。
	shared_ptr<A> pA44(new A[10], [](A *p){
		delete[] p;
	});

	//可用default_delete来做删除器,default_delete是标准库里的模板类。
	shared_ptr<A> pA(new A[10], std::default_delete<A[]>());

	//定义数字的时候我们在尖括号中加[]
	shared_ptr<A[]> pA55(new A[10]); // 在<>中加个[]就行了;
	shared_ptr<int[]> p77(new int[10]);
	//p77[0] = 12;
	//p77[1] = 15;

	shared_ptr<int> pintArr = make_shared_array<int>(5);
	shared_ptr<A> pAArr = make_shared_array<A>(15);

	//b) 指定删除器额外说明
	//就算是两个shared_ptr指定了不同的删除器,只要他们所指向的对象类型相同,那么这两个shared_ptr也属于同一类型;
	auto lambda1 = [](int *p){
		//日志。。
		delete p;
	};

	auto lambda2 = [](int *p){
		//日志。。。
		delete p;
	};
	shared_ptr<int> p99(new int(100), lambda1);
	shared_ptr<int> p100(new int(200), lambda2);
	p100 = p99; // p100 会先调用lambda2 把自己所指向的对象释放,然后指向p99所指向的对象。p99指向的对象引用计数为2;
		//整个main执行完毕后,还会调用lambda1来释放p99、p100共同指向的对象;

	p99.reset();

	//类型相同,就代表可以放到元素类型为该对象类型的容器里来;
	vector<shared_ptr<int>> pvec{ p99, p100 };

	//make_shared是提倡的生成shared_ptr的方法。但是make_shared这种方法,让我们没有办法指定自己的删除器;

	system("pause");
	return 0;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值