C++智能指针——shared_ptr/weak_ptr

|版本声明:山河君,未经博主允许,禁止转载

系列文章目录

C++11新特性大全+实例

前言

C++ 这门编程语言的历史可以追溯至 1979 年,当时的 Bjarne Stroustrup(C++ 之父,后续简称 Stroustrup)还在使用 Simula 语言进行开发工作。

1998 年,C++ 标准委员会发布了第一版 C++ 标准,并将其命名为 C++ 98 标准。据不知名人士透露,《带注释的C++参考手册》这本书对 C++ 98 标准的制定产生了很大的影响。

经过作者的不断迭代,一本书往往会先后发布很多个版本,其中每个新版本都是对前一个版本的修正和更新。C++ 编程语言的发展也是如此。截止到目前(2020 年),C++ 的发展历经了以下 3 个个标准:

2011 年,新的 C++ 11 标准诞生,用于取代 C++ 98 标准。

2014 年,C++ 14 标准发布,该标准库对 C++ 11 标准库做了更优的修改和更新;

2017 年底,C++ 17 标准正式颁布;

虽然学习 C++11 需要花些时间,但这是非常值得的;C++11 非常实用,它不但提高了开发效率,还让程序更加健壮和优雅。程序员应该乐于升级换代已有的知识,而学习和使用 C++11 早就是大势所趋。

一、shared_ptr介绍

1.shared_ptr特点

相对于unique_ptr持有对象独有权,即保证保证同一时间内只有一个智能指针可以指向该对象。

shared_ptr实现共享式拥有,即多个智能指针可以指向相同对象,该对象和其相关资源会在“最后一个引用(reference)被销毁”时候释放。

2.原理

(1)通过使用引用计数机制,shared_ptr可以共享所有权,但是这回产生额外的开销。

(2)shared_ptr 对象除了包括一个所拥有对象的指针外, 还包括一个引用计数代理对象的指针,用于进行计数。

(3)了保证线程安全性,引用计数器的加1,减1操作都是 原子操作,它保证 shared_ptr 由多个线程共享时不会爆掉。

(4)同样重载了解除引用运算符(*)和成员选择运算符(->)。

通俗来讲:就是说多个shared_ptr在管理同一个指针时,都会引用同一个计数器。每增加一个share_ptr管理该指针,计数器就会+1,每减少一个share_ptr,计数器就会-1;当计数器为0时,此时才会释放指针所指向的资源。

二、shared_ptr使用

1.构造以及简单使用

#include <iostream>
#include <vector>

using namespace std;

class Test {

public:
	Test() {
		str = "hello world";
		cout << "init" << endl;
	}

	void print()
	{
		cout << str << endl;
	}

	~Test()
	{
		cout << "realse" << endl;
	}

private:
	string str;
};

int main()
{
	Test* test = new Test;
	shared_ptr<Test> p1(test);
	shared_ptr<Test> p2 = p1;
	(*p1).print();
	(*p2).print();
	return 0;
}

结果:可以看到shared_ptr可以互相赋值,并且每个shared_ptr都可以使用,而在最后进行释放
在这里插入图片描述

2.常用方法

(1)use_count 返回引用计数的个数

(2)unique 返回是否是独占所有权( use_count 为 1)

(3)swap 交换两个 shared_ptr 对象(即交换所拥有的对象)

(4)reset 放弃内部对象的所有权或拥有对象的变更, 会引起原有对象的引用计数的减少

(5)get 返回内部对象(指针)

int main()
{
	Test* test = new Test;
	shared_ptr<Test> p1(test);
	cout << p1.use_count() << endl; //返回1
	shared_ptr<Test> p2 = p1;
	cout << p1.use_count() << endl; //返回2
	cout << p2.use_count() << endl; //返回2
	return 0;
}

3.错误用法

(1)存在两个计数器,会引起对同一块内存释放两次,发生段错误

int main()
{
	Test* test = new Test;
	shared_ptr<Test> p1(test);
	shared_ptr<Test> p2(test);
	return 0;
}

(2)循环引用,导致内存泄漏

#include <iostream>
#include <vector>

using namespace std;

class Test2;
class Test1 {

public:
	Test1() {
		cout << "init" << endl;
	}

	~Test1()
	{
		cout << "realse" << endl;
	}

public:
	shared_ptr<Test2> p1;
};

class Test2 {

public:
	Test2() {
		cout << "init" << endl;
	}

	~Test2()
	{
		cout << "realse" << endl;
	}

public:
	shared_ptr<Test1> p2;
};

int main()
{
	shared_ptr<Test1> p1(new Test1());
	shared_ptr<Test2> p2(new Test2());
	(*p1).p1 = p2;
	(*p2).p2 = p1;
	cout << p1.use_count() << endl;
	cout << p2.use_count() << endl;
	return 0;
}

结果:可以看到并没有自动释放内存,这是由于 test1和 test2对象互相引用,它们的引用计数都是 1,不能自动释放,并且此时这两个对象再无法访问到。这就引起“内存泄漏”。
在这里插入图片描述
要解决这类问题就要使用weak_ptr

三、weak_ptr介绍

1.weak_ptr特点

weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象. 进行该对象的内存管理的是那个强引用的 shared_ptr. weak_ptr只是提供了对管理对象的一个访问手段。

2.使用weak_ptr目的

weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。

四、weak_ptr的使用

1.方法

(1)weak_ptr 没有重载*和->但可以使用 lock 获得一个可用的 shared_ptr 对象.。注意, weak_ptr 在使用前需要检查合法性。

(2)expired 用于检测所管理的对象是否已经释放, 如果已经释放, 返回 true; 否则返回 false.

(3)lock 用于获取所管理的对象的强引用(shared_ptr). 如果 expired 为 true, 返回一个空的 shared_ptr; 否则返回一个 shared_ptr, 其内部对象指向与 weak_ptr 相同。

(4)use_count 返回与 shared_ptr 共享的对象的引用计数。

(5)reset 将 weak_ptr 置空。

(6)weak_ptr 支持拷贝或赋值, 但不会影响对应的 shared_ptr 内部对象的计数。

2.使用实例

(1)针对shared_ptr错误用法2,把类中shared_ptr改为weak_ptr

#include <iostream>
#include <vector>

using namespace std;

class Test2;
class Test1 {

public:
	Test1() {
		cout << "init" << endl;
	}

	~Test1()
	{
		cout << "realse" << endl;
	}

public:
	weak_ptr<Test2> p1;
};

class Test2 {

public:
	Test2() {
		cout << "init" << endl;
	}

	~Test2()
	{
		cout << "realse" << endl;
	}

public:
	weak_ptr<Test1> p2;
};

int main()
{
	shared_ptr<Test1> p1(new Test1());
	shared_ptr<Test2> p2(new Test2());
	(*p1).p1 = p2;
	(*p2).p2 = p1;
	cout << p1.use_count() << endl;
	cout << p2.use_count() << endl;

	return 0;
}

结果:可以看到计数器都为1
在这里插入图片描述
(2)查看是否释放,如果弱引用智能指针没有释放,则获取

int main()
{
	shared_ptr<Test1> p1(new Test1());
	shared_ptr<Test2> p2(new Test2());
	(*p1).p1 = p2;
	(*p2).p2 = p1;
	cout << p1.use_count() << endl;
	cout << p2.use_count() << endl;

	if(!(*p1).p1.expired())	//查看是否释放
		shared_ptr<Test2> p3 = (*p1).p1.lock();
	cout << p1.use_count() << endl;
	cout << p2.use_count() << endl;

	return 0;
}

结果:这样就可以进行正常释放内存了
在这里插入图片描述

总结

如果对您有用,不妨点个赞!谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值