16.3 shared_ptr常用操作、计数与自定义删除器等

一:shared_ptr引用计数的增加和减少

共享式引用计数,每一个shared_ptr的拷贝都指向相同的内容(对象),只有最后一个指向该对象的shared_ptr指针不需要。
在指向该对象的时候,这个shared_ptr才会去析构所指向的对象。
<1>引用计数的增加
每个 shared_ptr 都会记录有多少个其他的 shared_ptr 指向相同的对象
auto p6 = make_shared(100); //目前p6所指向的对象只有p6一个引用者
auto p7(p6); //智能指针定义时的初始化,p7和p6指向了相同的对象,此对象目前有两个引用者

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

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

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

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

二:shared_ptr的常用操作

<1>use_count():返回多少个智能指针指向某个对象,主要用于调式目的;

int main()
{
	shared_ptr<int> myp(new int(100));
	int icoutn = myp.use_count();
	shared_ptr<int> myp2(myp);
	icoutn = myp.use_count();
	shared_ptr<int> myp3;
	myp3 = myp2;
	icoutn = myp.use_count();
	return 0;
}

<2>unique():是否该智能指针独占某个指向的对象,也就是若只有一个智能指针指向某个对象,则unique() 返回 true,否则返回 false。

int main()
{
	shared_ptr<int> myp(new int(100));
	shared_ptr<int> myp2(myp);
	if (myp.unique())
	{
		cout << "unique ok" << endl;
	}
	else
	{
		cout << "unique  nonono" << endl;
	}

	shared_ptr<int> myp3;
	if (myp.unique())
	{
		cout << "unique ok" << endl;
	}
	else
	{
		cout << "unique  nonono" << endl;
	}
	return 0;
}

<3> reset():恢复(复位 / 重置)的意思。
1>reset不带参数时
若pi是唯一指向该对象的指针,那么释放pi所指向的对象,并将pi置空;
若pi不是唯一指向该对象的执行,那么不释放pi所指向的对象,但指向该对象的引用计数会减少1,同时将pi置空。

int main()
{
	shared_ptr<int> pi_1(new int(100));
	pi_1.reset();
	if (pi_1 == nullptr)
	{
		cout << "pi被置空" << endl;
	}
	shared_ptr<int> pi_2(new int(100));
	auto pi_3(pi_2);
	pi_2.reset();
	return 0;
}

2>reset()带参数(一般是new出来的指针)
若pi是唯一指向该对象的智能指针,则释放pi所指向的对象,让pi指向新对象;
若pi不是唯一指向该对象的指针,则不释放pi所指向的对象,但指向该对象的引用计数会减少1,同时让pi指向新对象。

int main()
{
	shared_ptr<int> pi(new int(100));
	auto pi2(pi);  // pi2引用计数为2
	pi.reset(new int(1));  // 释放原内存,指向新内存
	if (pi.unique())
	{
		cout << "unique ok" << endl;
	}
	else
	{
		cout << "unique  nonono" << endl;
	}
	if (pi2.unique())
	{
		cout << "unique ok" << endl;
	}
	else
	{
		cout << "unique  nonono" << endl;
	}
	return 0;
}

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

<4>*解引用:获取pi所指向的对象

int main()
{
	shared_ptr<int> pother(new int(123456));
	cout << *pother << endl;
	cout << typeid(*pother).name() << endl;
	return 0;
}

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

int main()
{
	shared_ptr<int> myp(new int(100));
	int* p = myp.get();
	*p = 45;
	delete p;  //千万不要这么干,不然系统会报告异常,产生不可预料的后果
	return 0;
}

<6>swap():交换两个指针所指向的对象

int main()
{
	shared_ptr<string> ps1(new string("I love China1"));
	shared_ptr<string> ps2(new string("I love China2"));
	swap(ps1, ps2);
	ps1.swap(ps2);
	return 0;
}

<7> = nullptr
1>将所指向的对象引用计数减1,若引用计数变为0,则释放智能指针所指向的对象。
2>将智能指针置空

int main()
{
	shared_ptr<string> ps1(new string("I love China1"));
	shared_ptr<string> ps2(ps1);
	ps1 = nullptr;
	return 0;
}

<8>智能指针名字作为判断条件

int main()
{
	shared_ptr<string> ps1(new string("I Love China"));
	ps1 = nullptr;
	if (ps1)
	{
		cout << "ps1指向一个对象" << endl;
	}
	else
	{
		cout << "ps1为空" << endl;
	}
	return 0;
}

<9>指定删除器以及数组问题
1>指定删除器
一定时机帮我们删除所指向的对象,delete:将delete运算符号作为默认的资源析构方式。
我们可以指定自己的删除器取代系统提供的默认删除器,当智能指针需要删除所指向的对象时,
编译器就会调用我们自己提供的删除器来删除;
shared_ptr指定的删除器方法比较简单,一般只需要在参数中添加具体的删除器函数名即可;

void myDelete(int* p)
{
	delete p;
}

int main()
{
	shared_ptr<int> p(new int(100), myDelete);
	shared_ptr<int> p2(p);  //两个引用计数
	p2.reset();  // 剩一个引用计数,p2位nullptr
	p.reset();  // 因为此时只有1个引用计数,所以要释放指向的对象,调用我们的删除器:mydelete,同时p置空。
	return 0;
}

删除器可以是一个lambda表达式
shared_ptr p2(new int(100), [](int* p) {
delete p;
});

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

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

可以用default_delete来做删除器,default_delete是标准库里的模板类。
shared_ptr pA(new A[10], std::default_delete<A[]>());
定义数组的时候我们在尖括号中加[]
shared_ptr<A[]> pA(new A[10]); //在<>中加个[]就行了。
shared_ptr<int[]> p(new int[10]);
p[0] = 12;
p[1] = 13;
shared_ptr pintArr = make_shared_array(5);
shared_ptr pintArr2 = make_shared_array(15);

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

auto lambda2 = [](int *p) {
//日志…
delete p;
};

shared_ptr p1(new int(100), lambda1);
shared_ptr p2(new int(200), lambda2);
p2 = p1; //p2会先调用lambda2把自己所指向的对象释放,然后指向p1所指向的对象,p1指向的对象引用计数为2;
整个main执行完毕后,还会调用lambda1来释放p1,p2共同指向的对象
p1.reset();
类型相同,就代表可以放到类型元素为该对象类型的容器里来
vector<shared_ptr> pvec{ p1, p2 };
make_shared:是提倡生成shared_ptr的方法,但是这种make_shared这种方法,让我们没有方法指定自己的删除器。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值