一、shared_ptr学习

1.shared_ptr和weak_ptr 基础概念

  • shared_ptr与weak_ptr智能指针均是C++ RAII的一种应用,可用于动态资源管理
  • shared_ptr基于“引用计数”模型实现,多个shared_ptr可指向同一个动态对象,并维护了一个共享的引用计数器,记录了引用同一对象的shared_ptr实例的数量。当最后一个指向动态对象的shared_ptr销毁时,会自动销毁其所指对象(通过delete操作符)。
  • shared_ptr的默认能力是管理动态内存,但支持自定义的Deleter以实现个性化的资源释放动作。
  • weak_ptr用于解决“引用计数”模型循环依赖问题,weak_ptr指向一个对象,并不增减该对象的引用计数器

2.shared_ptr的基本操作

#include <memory>
#include <iostream> struct Foo {
Foo() { std::cout << "Foo...\n"; }
~Foo() { std::cout << "~Foo...\n"; }
}; struct D {
//删除p所指向的Foo对象
void operator()(Foo* p) const {
std::cout << "Call delete for Foo object...\n";
delete p;
}
}; int main()
{
// constructor with no managed object
std::shared_ptr<Foo> sh1; // constructor with object
std::shared_ptr<Foo> sh2(new Foo);
std::shared_ptr<Foo> sh3(sh2);
std::cout << sh2.use_count() << '\n';
std::cout << sh3.use_count() << '\n'; //constructor with object and deleter
std::shared_ptr<Foo> sh4(new Foo, D());
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

构造方法: 
1.通过make_shared函数构造 
auto s_s = make_shared(“hello”);

2.通过原生指针构造 
int* pNode = new int(5); 
shared_ptr s_int(pNode); 
//获取原生指针 
int* pOrg = s_int.get();

3.通过赋值函数构造shared_ptr

4.重载的operator->, operator *,以及其他辅助操作如unique()、use_count(), get()等成员方法。

3 实验智能指针引用计数,增加和减少的规律

实验的主要内容有: 
1.shared_ptr变量在生命周期中销毁后,引用计数是否减1? 
2.shared_ptr作为函数参数,分为传值和传引用,引用计数如何变化? 
2.函数返回值为shared_ptr类型时,引用计数是否会变化?

带着这几个问题,我们来看下代码.

#include <iostream>
#include <memory>
using namespace std; void Func1(shared_ptr<int> a)
{
cout<<"Enter Func1"<<endl;
cout<<"Ref count: "<<a.use_count()<<endl;
cout<<"Leave Func1"<<endl;
} shared_ptr<int> Func2(shared_ptr<int>& a)
{
cout<<"Enter Func2"<<endl;
cout<<"Ref count: "<<a.use_count()<<endl;
cout<<"Leave Func2"<<endl;
return a;
} int main()
{
//构造一个指向int类型对象的指针aObj1,引用计数+1
shared_ptr<int> aObj1(new int(10));
cout<<"Ref count: "<<aObj1.use_count()<<endl; {
//同aObj1,不过由于生存周期在括号内,所以aObj2会被销毁
shared_ptr<int> aObj2 = aObj1;
cout<<"Ref count: "<<aObj2.use_count()<<endl;//引用计数-1
} //在调用函数时,参数为shared_ptr类型,参数为传值类型,智能指针引用计数+1
Func1(aObj1); //在调用函数时,参数为shared_ptr类型,参数为传引用类型,智能指针引用计数不变
Func2(aObj1); shared_ptr<int> aObj3 = Func2(aObj1);//引用计数+1
cout<<"Ref count:"<<aObj3.use_count()<<endl; return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

运行结果如下: 

有效的掌握好智能指针的引用计数的变化规律,才能把程序写的更好.

4. shared_ptr的应用场景以及使用注意事项

4.1 对象之间“共享数据”,对象创建与销毁“分离” 
4.2 放入容器中的动态对象,使用shared_ptr包装,比unique_ptr更合适 
4.3 管理“动态数组”时,需要制定Deleter以使用delete[]操作符销毁内存,因为shared_ptr并没有针对数组的特化版本(unique_ptr有针对数组的特化版本)

5.shared_ptr的线程安全问题

  1. 同一个shared_ptr被多个线程读,是线程安全的;
  2. 同一个shared_ptr被多个线程写,不是 线程安全的;
  3. 共享引用计数的不同的shared_ptr被多个线程写,是线程安全的。 
    对于第三点,我们一般采用: 
    对于线程中传入的外部shared_ptr对象,在线程内部进行一次新的构造,例如: sharedptr AObjTmp = outerSharedptrObj;

二、weak_ptr学习

我们先搞清楚,weak_ptr为什么出现,或者说它是为了解决什么问题而存在的(存在即合理),哈哈

class Parent
{
public:
shared_ptr<Child> child;
}; class Child
{
public:
shared_ptr<Parent> parent;
}; shared_ptr<Parent> pA(new Parent);
shared_ptr<Child> pB(new Child);
pA->child = pB;
pB->parent = pA;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

在Parent类中存储了指向Child类对象的智能指针成员变量,而在Child类中也存储了指向Parent类对象的智能指针成员变量,如此就会造成环形引用,这个成因在C++中很好解释.

要解决环形引用的问题,没有特别好的办法,一般都是在可能出现环形引用的地方使用weak_ptr来代替shared_ptr。说到了weak_ptr,那下面就接着总结weak_ptr吧。

下面我们来一起学习下weak_ptr这个东东

weak_ptr指向shared_ptr指针指向的对象的内存,却并不拥有该内存。 
但是,使用weak_ptr成员lock,则可返回其指向内存的一个shared_ptr对象,且在所指对象内存已经无效时,返回指针空值(nullptr)。由于weak_ptr是指向shared_ptr所指向的内存的,所以,weak_ptr并不能独立存在。

#include <iostream>
#include <memory>
using namespace std; void Check(weak_ptr<int> &wp)
{
shared_ptr<int> sp = wp.lock(); // 重新获得shared_ptr对象
if (sp != nullptr)
{
cout << "The value is " << *sp << endl;
}
else
{
cout << "Pointer is invalid." << endl;
}
} int main()
{
shared_ptr<int> sp1(new int(10));
shared_ptr<int> sp2 = sp1;
weak_ptr<int> wp = sp1; // 指向sp1所指向的内存 cout << *sp1 << endl;
cout << *sp2 << endl;
Check(wp); sp1.reset();
cout << *sp2 << endl;
Check(wp); sp2.reset();
Check(wp); system("pause");
return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

学习编程最好的方式就是一步步的跟踪去调试. 
借鉴上面的代码,我们在使用weak_ptr时也要当心,时刻需要判断weak_ptr对应的shared_ptr是否为空,weak_ptr并不会增加shared_ptr的引用计数.

另附一篇地址,讲解为何不同的shared_ptr对象可以被多线程同时修改(即使这些shared_ptr对象管理着同一个对象的指针)

https://blog.csdn.net/jiangfuqiang/article/details/8292906

最新文章

  1. 探索C#之系列目录导航
  2. SQLServer
  3. UIView常用的一些方法小记之setNeedsDisplay和setNeedsLayout
  4. Android学习笔记----解决“com.android.dex.DexIndexOverflowException: method ID not in [0, 0xffff]: 65536”问题
  5. web.xml文件中的web-app元素 部署
  6. Aimp3的播放列表 按评分排序 落雨
  7. Eclipse 卸载插件
  8. ServiceStack.Redis 破解
  9. wifi测试相关(iwconfig,WPA Supplicant用法)
  10. Fiddler 抓取eclipse中的请求
  11. 5.对象创建型模式-原型PROTOTYPE
  12. oracle设置自动增长序列
  13. SpringBoot进阶教程(二十六)整合Redis之共享Session
  14. qml: 组件复用
  15. js中关于Blob对象的介绍与使用
  16. asp.net gridview 如何实现行点击事件
  17. GUC-13 生产者消费者案例
  18. python 之 collections
  19. 递归中traverse小人 &amp; dc女王的区别
  20. python 类变量 在多线程下的共享与释放问题-乾颐堂

热门文章

  1. poj 2405 Beavergnaw
  2. 在create-react-app创建的React项目应用中配置JQ、Sass
  3. jquery 使用整理机制
  4. .net托管资源与非托管资源
  5. How to push master to QA branch in GIT
  6. .net IoC 之 Spring.Net 适合刚开始使用
  7. hdu 1561 树形背包 选k个最大价值
  8. wget http://pypi.python.org/packages/source/s/setuptools/setuptools-2.0.tar.gz 下载时报错 ssl is required 解决办法
  9. [Java反射基础四]通过反射了解集合泛型的本质
  10. 微信公众号开发《一》OAuth2.0网页授权认证获取用户的详细信息,实现自动登陆