智能指针的使用

  • 一.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 详解与示例

  1. 不要使用同一个原始指针构造 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仍然指向那块被释放的内存。

  1. 不要用栈中的指针构造 shared_ptr 对象

shared_ptr 默认的构造函数中使用的是delete来删除关联的指针,所以构造的时候也必须使用new出来的堆空间的指针。
示例:

   int x = 12;std::shared_ptr<int> ptr(&x);return 0;

当 shared_ptr 对象超出作用域调用析构函数delete 指针&x时会出错。

  1. 建议使用 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

  1. unique_ptr强调唯一性

相比于shared_ptrunique_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;
  1. 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()

  1. 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

  1. 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

通过结果可以看到,最后CACB的对象并没有被析构,其中的引用效果如下图所示,起初定义完ptr_aptr_b时,只有①③两条引用,然后调用函数set_ptr后又增加了②④两条引用,当test_refer_to_each_other这个函数返回时,对象ptr_aptr_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!

通过这次结果可以看到,CACB的对象都被正常的析构了,引用关系如下图所示,流程与上一例子相似,但是不同的是④这条引用是通过weak_ptr建立的,并不会增加引用计数,也就是说CA的对象只有一个引用计数,而CB的对象只有2个引用计数,当test_refer_to_each_other这个函数返回时,对象ptr_aptr_b被销毁,也就是①③两条引用会被断开,此时CA对象的引用计数会减为0,对象被销毁,其内部的m_ptr_b成员变量也会被析构,导致CB对象的引用计数会减为0,对象被销毁,进而解决了引用成环的问题。

智能指针(shared_ptr、unique_ptr、weak_ptr)的使用相关推荐

  1. 32. 对c++中的smart pointer四个智能指针shared_ptr,unique_ptr,weak_ptr,auto_ptr的理解

    C++里面的四个智能指针: auto_ptr, shared_ptr, weak_ptr, unique_ptr 其中后三个是c++11支持,并且第一个已经被11弃用. 智能指针的作用是管理一个指针, ...

  2. C++ 使用智能指针shared_ptr/unique_ptr管理数组

    目录 零.要管理的类 一.使用shared_ptr管理数组 二.使用unique_ptr管理数组 1.第一种方式 2.第二种方式 关于shared_ptr/unique_ptr的基础,我不在本篇博客中 ...

  3. C++11智能指针shared_ptr、weak_ptr、unique_ptr用法

    该博文为原创文章,未经博主同意不得转载,如同意转载请注明博文出处 本文章博客地址:https://cplusplus.blog.csdn.net/article/details/105065859 智 ...

  4. C++智能指针 shared_ptr、weak_ptr

    shared_ptr,共享指针应用于需要多个指针指向同一个对象的情况. 我们可以认为每个shared_ptr都有一个关联的计数器,通常称其为引用计数,无论何时我们拷贝一个shared_ptr,计数器都 ...

  5. 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 原创)智能指针拾遗 (原创)智能指 ...

  6. C++智能指针shared_ptr、unique_ptr以及weak_ptr

    目录 shared_ptr类 shared_ptr和unique_ptr都支持的操作 shared_ptr独有的操作 make_shared函数 shared_ptr自动销毁所管理的对象 由普通指针管 ...

  7. 智能指针shared_ptr的用法

    智能指针shared_ptr的用法 2016-12-03 15:39 by jiayayao, 360 阅读, 0 评论, 收藏, 编辑 为了解决C++内存泄漏的问题,C++11引入了智能指针(Sma ...

  8. 智能指针shared_ptr

    如果有可能就使用unique_ptr,然后很多时候对象是需要共享的,因此shared_ptr也就会用得很多.shared_ptr允许多个指向同一个对象,当指向对象的最后一个shared_ptr销毁时, ...

  9. 智能指针shared_ptr的几个例子

    #include <string> #include <iostream> #include <memory> //智能指针定义在头文件memory中,例如shar ...

最新文章

  1. css3动画:animation
  2. jquery-uploadifyv3.2.1 文件上传插件 学习
  3. SAP CRM Interactive Report(交互式报表)里和服务订单相关的一些字段
  4. js判断函数是否存在
  5. 关于maven各种报错
  6. 青岛理工大学QUT期末考试《电子商务概论》思维导图
  7. kdj买卖指标公式源码_长短KDJ源码与kdj顶底背离指标公式(附图)
  8. Windows10玩转Linux子系统(WSL)
  9. 基于CNN的动态手势识别:Real-time Hand Gesture Detection and Classification Using Convolutional Neural Networks
  10. 如何在 Mac 中隐藏文件或文件夹?
  11. 微信用户绑定java实例_第三方网站微信登录java代码实现
  12. 什么高大填空四个字动人_照样子填空填四字成语什么什么什么地想
  13. WebRTC RTCP PS Feedback
  14. 查看话单日志和性能日志有效信息的流程
  15. 线性代数-4-向量组的线性相关性
  16. 微信自动抢红包的实现(Demo已增加查看TopActivity功能)
  17. 3dmath 直线于圆柱的交点(无限长圆柱)
  18. 老男孩python培训班
  19. 【学习笔记】3篇关于表情识别的文章
  20. 科技政策 | 《深圳市加快加快推动人工智能高质量发展高水平应用行动方案(2023—2024年)》发布

热门文章

  1. 开源云计算厂商:浅析渠道激活平台的打造
  2. STC 模拟eeprom数据丢失
  3. 小程序的gridview布局
  4. 计算机二级C语言100套题
  5. 【百度​Sugar BI​ - 自动分析】你负责准备数据,我负责生成报表题
  6. unity基础学习之法线贴图
  7. 服务器阵列信息恢复,DELL RAID阵列的创建恢复回原有的用户数据
  8. 验证身份证号码是否合法以及信息提取
  9. canvas在图片上标记后保存到后台
  10. 逆向——iPhone越狱历程