27.C++- 智能指针

智能指针

  • 在C++库中最重要的类模板之一
  • 智能指针实际上是将指针封装在一个类里,通过对象管理指针.

STL中的智能指针auto_ptr

头文件: <memory>

  • 生命周期结束时,自动摧毁指向的内存空间
  • 不能指向堆数组(因为auto_ptr的析构函数删除指针用的是delete,而不是delete[])
  • auto_ptr的构造函数为explicit类型,所以只能显示初始化,比如:
  auto_ptr<int> ap1(new int(1));      //初始化正确,创建ap1类模板对象,使类模板里的指针为int*型,并指向1的地址

  int* p = new int(1);
  auto_ptr<int> ap2(p);               //初始化正确


  // auto_ptr<int> ap3 = new int(2);  //出错,不能隐式初始化
  • 提供get()成员函数,可以用来查看类里的指针地址.比如:
  auto_ptr<int> ap(new int(1));         
  cout<< ap.get()<<endl;          //打印数值1的地址 : 0x6d2d18
      

  int *p =ap.get();
  cout<< *p<<endl;                  //打印数值1
  • 一片堆空间只属于一个智能指针对象(因为多个指向相同地址的智能指针调用析构函数时,会出现bug)
  • 当auto_ptr被拷贝或赋值后,则自身的指针指向的地址会被抢占,比如:
  auto_ptr<int> p1(new int(1));
  auto_ptr<int> p2(new int(2));

  p1 =p2;                                    //首先会delete p1对象的类成员指针,然后将p2对象的类成员指针赋值给p1, 最后修改p2指针地址为NULL

  cout<<"p2 ="<<p2.get()<<endl;             //打印 : p2=0

  //cout<<*p2<<endl;                        //出错,因为p2=0

初探auto_ptr智能指针

#include <iostream>
#include <memory>

using namespace std;

class Test
{
public:
       int mvalue;
       Test(int i=0)
       {
              mvalue=i;
              cout<< "Test("<<mvalue<<")"<<endl;
       }

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

void func()                           //在func函数里使用auto_ptr
{
       auto_ptr<Test> p1(new Test(1));
       cout<<"p1 ="<<p1.get()<<endl;

       cout<<endl;

       auto_ptr<Test> p2(new Test(2));
       cout<<"p2 ="<<p2.get()<<endl;

       cout<<endl;

       cout<<"p1=p2"<<endl;
       p1=p2;   

       cout<<endl;

       cout<<"p1 ="<<p1.get()<<endl;
       cout<<"p2 ="<<p2.get()<<endl;
}

int main()
{  
       cout<<"*****begin*****"<<endl;
       func();  
       cout<<"*****end*****"<<endl;

       return 0;
}

运行打印:

*****begin*****
Test(1)
p1 =0x8db1008

Test(2)
p2 =0x8db1018

p1=p2
~Test(1)

p1 =0x8db1018
p2 =0
~Test(2)
*****end*****

从结果可以看到,由于func()的生命周期结束,所以里面的auto_ptr指针自动就被释放了。

可以发现在调用p1=p2时, 首先会delete p1对象的类成员指针(调用~Test(1)析构函数),然后将p2对象的类成员指针赋值给p1(p1=0x8db1018), 最后修改p2指针地址为NULL(p2 =0)

STL中的智能指针shared_ptr(需要C++11支持)

  • 带有引用计数机制,支持多个指针对象指向同一片内存(实现共享)
  • 提供swap()成员函数,用来交换两个相同类型的对象,比如:
  shared_ptr<int> p1(new int(1));
  shared_ptr<int> p2(new int(2));

  p1.swap(p2);                  //交换后 p1=2,p2=1

  cout<< *p1 <<endl;            //打印 2
  cout<< *p2 <<endl;            //打印 1
  • 提供unique()成员函数, 判断该指针对象地址是否被其它指针对象引用
  • 提供get()成员函数,用来获取指针对象指向的地址
  • 提供reset()成员函数,将自身指针对象地址设为NULL,并将引用计数-1(当计数为0,会自动去delete内存)
  • 提供use_count()成员函数,可以用来查看引用计数个数,比如:
  shared_ptr<int> sp1(new int(30));      //计数+1
  cout<<sp1.use_count()<<endl;           //打印计数:1
  cout<<sp1.unique()<<endl;              //打印:1
  

  shared_ptr<int> sp2(sp1);               //计数+1
  cout<<sp1.use_count()<<endl;            //打印:2
  cout<<sp1.unique()<<endl;               //由于sp1指针对象被sp2引用,打印:0


  sp1.reset();                            //将sp1指针对象地址设为NULL,计数-1

  cout<<sp1.get()<<endl;                  //sp1指针对象地址为NULL,打印:0

  cout<<sp2.use_count()<<endl;            //打印:1

  cout<<sp2.unique()<<endl;               //由于sp1释放,仅剩下sp2指向30所在的地址,所以打印:1

初探shared_ptr智能指针(以上个Test类为例分析)

#include <iostream>
#include <memory>

using namespace std;

class Test
{
public:
       int mvalue;
       Test(int i=0)
       {
              mvalue=i;
              cout<< "Test("<<mvalue<<")"<<endl;
       }

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

int main()
{  
       cout<<"*****begin*****"<<endl;

       shared_ptr<Test> p1(new Test(1));
       shared_ptr<Test> p2(p1);

       cout<<"*p1="<< p1->mvalue<<","<<"*p2="<<p2->mvalue<<endl;

       p1.reset();
       p2.reset();     

       cout<<"count:"<<p2.use_count()<<endl;

       cout<<"*****end*****"<<endl;
       return 0;
}

运行打印:

*****begin*****
Test(1)
*p1=1, *p2=1
~Test(1)
count:0
*****end*****

从结果可以看到,我们把p1和p2都释放了后,由于count=0,便自动去delete Test指针了.

STL中的其它智能指针(在后面学习到,再来深入描述)

-weak_ptr

  • 配合shared_ptr而引入的一种智能指针

-unique_ptr

  • 只能一个指针对象指向一片内存空间(和auto_ptr类似),但是不能被拷贝和赋值(实现唯一性)

QT中的智能指针(在后面学习到,再来深入描述)

-QPointer

头文件<QPointer>

  • 当其指向的对象被销毁时,他会被自动置空(避免被多次释放和野指针)
  • 缺点在于,该模板类析构时,不会自动摧毁所指向的对象(需要手工delete)

-QSharedPointer

头文件<QSharedPointer>

  • 带有引用计数机制,支持多个指针对象指向同一片内存(实现共享)
  • 可以被自由地拷贝和赋值
  • 当引用计数为0(最后一个指针被摧毁)时,才删除指向的对象(和shared_ptr类似)

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏zingpLiu

常用七种排序的python实现

算法复杂度分为时间复杂度和空间复杂度。其中, 时间复杂度是指执行算法所需要的计算工作量;而空间复杂度是指执行这个算法所需要的内存空间。

905
来自专栏ml

NYOJ-------表达式求值

时间限制:3000 ms | 内存限制:65535 KB 难度:3 描述 Dr.Kong设计的机器人卡多掌握了加减法运算以后,最近又学会了一些简单的函数求...

39110
来自专栏菜鸟计划

javascript 闭包详解

一、什么是匿名函数 创建一个函数并将它赋值给变量functionName,这种情况下创建的函数,即匿名函数。(函数表达式就是匿名函数) 二、闭包 1.什么是闭包...

3447
来自专栏xx_Cc的学习总结专栏

iOS底层原理总结 - 探寻block的本质(二)

2844
来自专栏DannyHoo的专栏

OC中内存管理的一些问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010105969/article/details/...

1353
来自专栏余林丰

多个构造器参数使用构建器

标题一眼看过去可能不是很明白要讲什么,先来看看下面一段代码。 1 package example; 2 3 /** 4 * 重叠构造器 5 * ...

1938
来自专栏Ldpe2G的个人博客

Scala typeclass 设计模式

本文的写作的灵感主要是看了这个视频 : Tutorial: Typeclasses in Scala with Dan Rosen

1566
来自专栏绿巨人专栏

[Java] 设计模式: Code Shape - 管理你的代码结构

4246
来自专栏AndroidTv

学点Groovy来理解build.gradle代码

在写这篇博客时,搜索参考了很多资料,网上对于 Groovy 介绍的博客已经特别多了,所以也就没准备再详细的去介绍 Groovy,本来也就计划写一些自己认为较重要...

3968
来自专栏数据结构与算法

病毒

【问题描述】   有一天,小y突然发现自己的计算机感染了一种病毒!还好,小y发现这种病毒很弱,只是会把文档中的所有字母替换成其它字母,但并不改变顺序,也不会增加...

5197

扫码关注云+社区

领取腾讯云代金券