智能指针(shared_ptr、unique_ptr、weak_ptr)的使用
智能指针的使用
- 一.shared_ptr
- 1.创建一个shared_ptr
- 2.shared_ptr的常用成员函数
- reset成员函数的使用
- 3.==注意事项==
- 二.unique_ptr
- 1.unique_ptr区别于shared_ptr
- 2.unique_ptr的成员函数与std::move()
- 3. 函数返回unique_ptr
- 三.weak_ptr
- 1. 使用weak_ptr解决循环引用的问题
C++中的智能指针实现是通过一个类来管理实际上的指针
,这个类要具备指针的基本操作。
一.shared_ptr
1.创建一个shared_ptr
std::shared_ptr<int> ptra = std::make_shared<int>(20);std::shared_ptr<int> ptra1(new int(20));
二者的区别:
前者std::make_shared
一次性为int
对象和用于引用计数的数据都分配了内存;后者new操作符只是为int分配了内存,引用计数会在构造函数中再做处理。
注意: 后者千万不能写成:
std::shared_ptr<int> ptra1 = new int(20);
因为你将一个int指针赋值给一个类对象,肯定不行。其实很好理解,这种情况下我们要调用的是构造函数,而不是赋值构造。
而前者写成:
std::shared_ptr<int> ptra(std::make_shared<int>(20));
也是可以的,均是调用拷贝构造函数。
2.shared_ptr的常用成员函数
为方便说明,我生成下面这样的一个shared_ptr类,表格中的说明均基于此。
string* ps = new string("hello");
shared_ptr<string> p(ps);
成员函数 | 说明 |
---|---|
p.use_count() | 返回和p共享的类对象个数 |
p.unique() | 若p.use_count() == 1 时,返回true |
*p | 等同 *ps,为hello |
p->length() | 等同ps->length(),返回string对象的长度 |
p | 若p指向一个对象,则返回true |
测试代码如下:
#include <iostream>
#include <string>
#include <memory>using namespace std;int main()
{string s = "hello";//"生成两个指针:" std::shared_ptr<string> ptrs = std::make_shared<string>(s);std::shared_ptr<string> ptrs2(ptrs); //拷贝构造,此时ptrs和ptrs2共同"指向"a的内存空间// "打印引用计数:"std::cout << ptrs.use_count() << std::endl;//引用计数为2std::cout << ptrs2.use_count() << std::endl;//引用计数为2//"改变ptrs2的指向:ptrs2 = std::make_shared<string>("world");//改变ptra2的指向//"打印此时的引用计数:"std::cout << ptrs.use_count() << std::endl;//引用计数为1std::cout << ptrs2.use_count() << std::endl;//引用计数为1//"unique使用:"if (ptrs.unique())cout << "ptrs的引用计数为1" << endl;//"*的使用:" std::cout << *ptrs << std::endl;//"->的使用:"std::cout << ptrs->length() << std::endl;if (ptrs)std::cout<<"ptrs is not empty" << std::endl;if (ptrs2)std::cout << "ptrs2 is not empty" << std::endl;system("pause");return 0;
}
结果:
reset成员函数的使用
尽管reset有4种形式,但下面只介绍基本的两种。
基本的两种形式 | 说明 |
---|---|
void reset() noexcept; | 将调用的对象清空,即变成一个空指针 |
template < class T> void reset (T* p); | 使调用的shared_ptr获得p的所有权 |
测试如下:
#include <iostream>
#include <string>
#include <memory>using namespace std;int main()
{std::shared_ptr<int> sp; // emptysp.reset(new int); // takes ownership of pointer*sp = 10;std::cout << *sp << '\n';sp.reset(new int); // deletes managed object, acquires new pointer*sp = 20;std::cout << *sp << '\n';sp.reset(); // deletes managed objectcout << sp.use_count() << endl;system("pause");return 0;
}
3.注意事项
以下内容来自:C++ 智能指针 shared_ptr 详解与示例
不要使用同一个原始指针构造 shared_ptr
创建多个 shared_ptr 的正常方法是使用一个已存在的shared_ptr 进行创建,而不是使用同一个原始指针进行创建,如下所示:
int *num = new int(23);std::shared_ptr<int> p1(num);std::shared_ptr<int> p2(p1); // 正确使用方法std::shared_ptr<int> p3(num); // 不推荐
在上面的使用中,num被p1和p3两个shared_ptr类管理,并且不像p1与p2关系是共享num一样,p1和p3是相互独立的,分别有独立的引用计数。这样当p1的引用计数为0而释放内存时,p3仍然指向那块被释放的内存。
不要用栈中的指针构造 shared_ptr 对象
shared_ptr 默认的构造函数中使用的是delete来删除关联的指针,所以构造的时候也必须使用new出来的堆空间的指针。
示例:
int x = 12;std::shared_ptr<int> ptr(&x);return 0;
当 shared_ptr 对象超出作用域调用析构函数delete 指针&x时会出错。
建议使用 make_shared
为了避免以上两种情形,建议使用make_shared()创建 shared_ptr 对象,而不是使用默认构造函数创建。
std::shared_ptr<int> ptr_1 = make_shared<int>();
std::shared_ptr<int> ptr_2 (ptr_1);
另外不建议使用get()函数获取 shared_ptr 关联的原始指针,因为如果在 shared_ptr 析构之前手动调用了delete函数,同样会导致类似的错误。
二.unique_ptr
1.unique_ptr区别于shared_ptr
unique_ptr强调唯一性
相比于shared_ptr,unique_ptr对某一个原始指针有“唯一”的管理权,即某个指针有且只能有一个unique_ptr来管理它
, 所以也就没有shared_ptr的引用计数机制。和shared_ptr一样,不要使用同一个原始指针构造unique_ptr。试想一下若我们做了如下操作会怎样?
{int *pa = new int(5);//两个对象来管理paunique_ptr<int> u(pa);unique_ptr<int> u1(pa);
}
很明显会两次调用unique_ptr的析构函数,那么pa指向的内存就被析构了两次。
为了实现上述的“唯一”,unique_ptr禁止了拷贝构造和赋值构造
。标准库的实现如下:
unique_ptr(const _Myt&) = delete;
_Myt& operator=(const _Myt&) = delete;
unique_ptr的初始化只能用new的形式
不像shared_ptr,可以用因为std::make_shared()
来返回一个shared_ptr类, unique_ptr*的初始化 只能用new的形式,当然也可以生成一个空的类对象,如下:
unique_ptr<int> u(new int(5));
unique_ptr<int> u1;
当然在C++14中,已经支持使用 std::make_unique
创建对象,就和std::make_shared()
一样。
2.unique_ptr的成员函数与std::move()
std::move()
由于unique_ptr禁止了拷贝构造和赋值构造,C++提供一个move函数,它的作用是移动。其实就和我们操作文件时一样,移动后源地址将清空。如下:
unique_ptr<int> u(new int(5));cout << *u << endl;if (u)cout << "u is not empty" << endl;unique_ptr<int> u1 = std::move(u);//将u移动给u1cout << *u1 << endl;if (!u)cout << "u is empty" << endl;
参考:http://www.cplusplus.com/reference/utility/move/?kw=move
unique_pt常用的成员函数
对于 * 、->以及判断 unique_ptr 对象是否为空这些操作,和shared_ptr是一样的,这里不再说明。下面是 unique_ptr 的release和reset操作。
成员函数 | 说明 |
---|---|
u.release() | u释放对指针的控制权,并返回管理的指针,同时将u置空 |
u.reset() | 做两步动作,1是delete掉u管理的指针,2是将u置空 |
u.reset(q) | 做两步动作,1是delete掉u当前管理的指针,2是将q的控制权给u |
如下所示:
int *pa = new int(5);unique_ptr<int> u(pa);//u->paunique_ptr<int> u1(u.release());//u1->pacout << *u1 << endl;//打印结果为5unique_ptr<int> u2(new int(10));//u2->new int(10)u1.reset(u2.release());//u1->new int(10),并且delete pacout << *pa << endl;//打印结果为任意未知数cout << *u1 << endl;//打印结果为10
3. 函数返回unique_ptr
前面说unique_ptr禁止了拷贝构造和赋值构造。但是它却可以拷贝或赋值一个即将被销毁的*unique_ptr*对象
。最常见的就是函数内部的一个局部对象。
如下所示:
std::unique_ptr<int> clone(int p)
{return unique_ptr<int>(new int(p));
}std::unique_ptr<int> clone2(int p)
{unique_ptr<int> ret(new int(p));return ret;
}
参考:C++ primer 第5版 12.1 P418
三.weak_ptr
以下内容引用至:C++11中智能指针的原理、使用、实现
weak_ptr是为了配合shared_ptr而引入的一种智能指针,因为它不具有普通指针的行为,没有重载operator*和->,它的最大作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况
。
weak_ptr可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。
使用weak_ptr的成员函数use_count()可以观测资源的引用计数,另一个成员函数expired()的功能等价于use_count()==0,但更快,表示被观测的资源(也就是shared_ptr的管理的资源)已经不复存在。
weak_ptr可以使用一个非常重要的成员函数lock()从被观测的shared_ptr获得一个可用的shared_ptr对象, 从而操作资源。但当expired()==true的时候,lock()函数将返回一个存储空指针的shared_ptr。
1. 使用weak_ptr解决循环引用的问题
以下内容引用至:智能指针(三):weak_ptr浅析
定义两个类,每个类中又包含一个指向对方类型的智能指针作为成员变量,然后创建对象,设置完成后查看引用计数后退出,看一下测试结果:
class CB;
class CA
{public:CA() { cout << "CA() called! " << endl; }~CA() { cout << "~CA() called! " << endl; }void set_ptr(shared_ptr<CB>& ptr) { m_ptr_b = ptr; }void b_use_count() { cout << "b use count : " << m_ptr_b.use_count() << endl; }void show() { cout << "this is class CA!" << endl; }
private:shared_ptr<CB> m_ptr_b;
};class CB
{public:CB() { cout << "CB() called! " << endl; }~CB() { cout << "~CB() called! " << endl; }void set_ptr(shared_ptr<CA>& ptr) { m_ptr_a = ptr; }void a_use_count() { cout << "a use count : " << m_ptr_a.use_count() << endl; }void show() { cout << "this is class CB!" << endl; }
private:shared_ptr<CA> m_ptr_a;
};void test_refer_to_each_other()
{shared_ptr<CA> ptr_a(new CA());shared_ptr<CB> ptr_b(new CB());cout << "a use count : " << ptr_a.use_count() << endl;cout << "b use count : " << ptr_b.use_count() << endl;ptr_a->set_ptr(ptr_b);ptr_b->set_ptr(ptr_a);cout << "a use count : " << ptr_a.use_count() << endl;cout << "b use count : " << ptr_b.use_count() << endl;
}
测试结果如下:
CA() called!
CB() called!
a use count : 1
b use count : 1
a use count : 2
b use count : 2
通过结果可以看到,最后CA
和CB
的对象并没有被析构,其中的引用效果如下图所示,起初定义完ptr_a
和ptr_b
时,只有①③两条引用,然后调用函数set_ptr
后又增加了②④两条引用,当test_refer_to_each_other
这个函数返回时,对象ptr_a
和ptr_b
被销毁,也就是①③两条引用会被断开,但是②④两条引用依然存在,每一个的引用计数都不为0,结果就导致其指向的内部对象无法析构,造成内存泄漏。
解决这种状况的办法就是将两个类中的一个成员变量改为weak_ptr
对象,因为weak_ptr
不会增加引用计数,使得引用形不成环,最后就可以正常的释放内部的对象,不会造成内存泄漏,比如将CB
中的成员变量改为weak_ptr
对象,代码如下:
class CB
{public:CB() { cout << "CB() called! " << endl; }~CB() { cout << "~CB() called! " << endl; }void set_ptr(shared_ptr<CA>& ptr) { m_ptr_a = ptr; }void a_use_count() { cout << "a use count : " << m_ptr_a.use_count() << endl; }void show() { cout << "this is class CB!" << endl; }
private:weak_ptr<CA> m_ptr_a;
};
测试结果如下:
CA() called!
CB() called!
a use count : 1
b use count : 1
a use count : 1
b use count : 2
~CA() called!
~CB() called!
通过这次结果可以看到,CA
和CB
的对象都被正常的析构了,引用关系如下图所示,流程与上一例子相似,但是不同的是④这条引用是通过weak_ptr
建立的,并不会增加引用计数,也就是说CA
的对象只有一个引用计数,而CB
的对象只有2个引用计数,当test_refer_to_each_other
这个函数返回时,对象ptr_a
和ptr_b
被销毁,也就是①③两条引用会被断开,此时CA
对象的引用计数会减为0,对象被销毁,其内部的m_ptr_b
成员变量也会被析构,导致CB
对象的引用计数会减为0,对象被销毁,进而解决了引用成环的问题。
智能指针(shared_ptr、unique_ptr、weak_ptr)的使用相关推荐
- 32. 对c++中的smart pointer四个智能指针shared_ptr,unique_ptr,weak_ptr,auto_ptr的理解
C++里面的四个智能指针: auto_ptr, shared_ptr, weak_ptr, unique_ptr 其中后三个是c++11支持,并且第一个已经被11弃用. 智能指针的作用是管理一个指针, ...
- C++ 使用智能指针shared_ptr/unique_ptr管理数组
目录 零.要管理的类 一.使用shared_ptr管理数组 二.使用unique_ptr管理数组 1.第一种方式 2.第二种方式 关于shared_ptr/unique_ptr的基础,我不在本篇博客中 ...
- C++11智能指针shared_ptr、weak_ptr、unique_ptr用法
该博文为原创文章,未经博主同意不得转载,如同意转载请注明博文出处 本文章博客地址:https://cplusplus.blog.csdn.net/article/details/105065859 智 ...
- C++智能指针 shared_ptr、weak_ptr
shared_ptr,共享指针应用于需要多个指针指向同一个对象的情况. 我们可以认为每个shared_ptr都有一个关联的计数器,通常称其为引用计数,无论何时我们拷贝一个shared_ptr,计数器都 ...
- C++11智能指针(unique_ptr、shared_ptr、weak_ptr)boost::scoped_ptr
C++11智能指针(unique_ptr.shared_ptr.weak_ptr)_-码农小非-的专栏-CSDN博客_c++ shared_ptr weak_ptr 原创)智能指针拾遗 (原创)智能指 ...
- C++智能指针shared_ptr、unique_ptr以及weak_ptr
目录 shared_ptr类 shared_ptr和unique_ptr都支持的操作 shared_ptr独有的操作 make_shared函数 shared_ptr自动销毁所管理的对象 由普通指针管 ...
- 智能指针shared_ptr的用法
智能指针shared_ptr的用法 2016-12-03 15:39 by jiayayao, 360 阅读, 0 评论, 收藏, 编辑 为了解决C++内存泄漏的问题,C++11引入了智能指针(Sma ...
- 智能指针shared_ptr
如果有可能就使用unique_ptr,然后很多时候对象是需要共享的,因此shared_ptr也就会用得很多.shared_ptr允许多个指向同一个对象,当指向对象的最后一个shared_ptr销毁时, ...
- 智能指针shared_ptr的几个例子
#include <string> #include <iostream> #include <memory> //智能指针定义在头文件memory中,例如shar ...
最新文章
- css3动画:animation
- jquery-uploadifyv3.2.1 文件上传插件 学习
- SAP CRM Interactive Report(交互式报表)里和服务订单相关的一些字段
- js判断函数是否存在
- 关于maven各种报错
- 青岛理工大学QUT期末考试《电子商务概论》思维导图
- kdj买卖指标公式源码_长短KDJ源码与kdj顶底背离指标公式(附图)
- Windows10玩转Linux子系统(WSL)
- 基于CNN的动态手势识别:Real-time Hand Gesture Detection and Classification Using Convolutional Neural Networks
- 如何在 Mac 中隐藏文件或文件夹?
- 微信用户绑定java实例_第三方网站微信登录java代码实现
- 什么高大填空四个字动人_照样子填空填四字成语什么什么什么地想
- WebRTC RTCP PS Feedback
- 查看话单日志和性能日志有效信息的流程
- 线性代数-4-向量组的线性相关性
- 微信自动抢红包的实现(Demo已增加查看TopActivity功能)
- 3dmath 直线于圆柱的交点(无限长圆柱)
- 老男孩python培训班
- 【学习笔记】3篇关于表情识别的文章
- 科技政策 | 《深圳市加快加快推动人工智能高质量发展高水平应用行动方案(2023—2024年)》发布