智能指针的学习

在动态内存管理中是使用delete和new 实现,单是动态内存管理经常会出现两种情况,一种是忘记释放内存,会造成内存泄漏;一种是尚有指针引用内存的情况下释放它,就回生成非法的指针(悬空指针)

智能指针的行为类似常规指针,重要的区别是它负责自动释放所指向的对象。标准库提供的两种智能指针的区别在于管理底层指针的方法不同,shared_ptr允许多个指针指向同一个对象,unique_ptr则“独占”所指向的对象。标准库还定义了一种名为weak_ptr的伴随类,它是一种弱引用,指向shared_ptr所管理的对象,这三种智能指针都定义在memory头文件中。

智能指针本质就是一个类模板。它可以创建任意的类型的指针对象,当智能指针对象使用完后,对象就会自动调用析构函数去释放该指针指向的空间

template<class T >
class smartptr//自定义指针
{
  public:
   smartptr(T*_ptr);//构造函数
     :ptr(_ptr)
     {}
     ~smartptr()//析构函数
     {
       if(ptr != nullptr)
       {
         cout << "smartptr:delete" << endl;
         delete ptr;
         ptr = nullptr;
       }
     }
     
     //智能指针可以使用* 和->去获取指针对象,我们对这两个符号进行重载
     T & operator*()
     {
       return *ptr
     }
     
     T *operator->()
     {
       return ptr
     }
     
private:
   T* ptr;//指针对象
}

shared_ptr

1、shared_ptr采用的是引用计数原理来实现多个shared_ptr对象之间共享资源
2、指针内部维护这一个引计数,用来记录这份资源被多少个对象使用

该指针由两部分组成
1、一个是指向堆上创建的对象的裸指针raw_ptr
2、一个指向内部隐藏的、共享的管理对象:share_count_object,其中use_count函数用于获取堆上对象被多少对象引用了,就是计数

创建智能指针是必须提供额外的信息,指针可以指向的类型
在这里插入图片描述

shared_ptr<string> p1;
shared_ptr<list<int>>p2;

默认初始化的智能指针中保存着一个空指针。
智能指针的使用方式和普通指针类似,解引用一个智能指针返回它指向的对象,在一个条件判断中使用智能指针就是检测它是不是空。

if(p1  && p1->empty())
	*p1 = "hi";

如下表所示是shared_ptr和unique_ptr都支持的操作:
在这里插入图片描述
如下表所示是shared_ptr特有的操作:
在这里插入图片描述
make_shared函数:

最安全的分配和使用动态内存的方法就是调用一个名为make_shared的标准库函数,此函数在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr。头文件和share_ptr相同,在memory中
必须指定想要创建对象的类型,定义格式见下面例子


//生成一个指向值为42的内存空间的shared_ptr指针
shared_ptr<int> p3 = make_shared<int>(42);

//生成一个指向值10个9的内存空间的shared_ptr指针
shared_ptr<string> p4 = make_shared<string>(10,'9');

//生成一个指向值为0的内存空间的shared_ptr指针
shared_ptr<int> p5 = make_shared<int>();

shared_ptr和new结合使用

shared_ptr<double> p1;

shared_ptr<int> p2(new int(42));

我们不能将一个内置指针隐式转换为一个智能指针

shared_ptr<int> p1 = new int (1024);//错误:必须使用直接初始化形式

shared_ptr<int> p2(new int(1024));//正确:使用了直接初始化



shared_ptr<int> clone(int p){
  return new int(p); //错误:隐式转换

}

shared_ptr<int> clone(int p){
  return shared_ptr<int>(new int(p)); /正确:显式

}

初始化

// 初始化方式1
std::shared_ptr<int> p1(new int(1)); 

// 初始化方式2
std::shared_ptr<int> p2 = p1; 

// 初始化方式3
std::shared_ptr<int> p3; 
p3.reset(new int(1));
if(p3) {
	cout << "p3 is not null";
}

// 初始化方式4
auto sp1 = make_shared<int>(100); //相当于 shared_ptr<int> sp1(new int(100));

推荐使用make_shared创建智能指针,高效

use_count ()获得当前观察资源的引用计数

#include <iostream>
#include <memory>
using namespace std;

void test(shared_ptr<int> sp) {
    // 引用计数此时应该是2
    cout << "test sp.use_count()" <<sp.use_count() << endl;
}

int main()
{
    std::shared_ptr<int> p1;
    p1.reset(new int(1));  	// 分配资源
    std::shared_ptr<int> p2 = p1;

    // 引用计数此时应该是2
    cout << "p2.use_count() = " << p2.use_count()<< endl;  // 2
  
    // 引用计数此时应该是1
    cout << "p2.use_count()= " << p2.use_count() << endl;  // 1
    
    p1.reset();   			// 释放资源
    // 智能指针可以通过重载的 bool 类型操作符来判断
    if(!p1) {  
        cout << "p1 is empty\n";
    }
    if(!p2) { 
        cout << "p2 is empty\n";
    }
    
    p2.reset();			 	// 释放资源
    // 引用计数此时应该是0
    cout << "p2.use_count()= " << p2.use_count() << endl; // 0
    if(!p2) {
        cout << "p2 is empty\n";
    }

    shared_ptr<int> sp5(new int(100));
    test(sp5);
    // 引用计数此时应该是1
    cout << "sp5.use_count()" << sp5.use_count() << endl;
    return 0;
}

weak_ptr

shared_ptr虽然好用但是也会有缺点(循环引用)
假设我们要使用定义一个双向链表,如果我们想要让创建出来的链表的节点都定义成shared_ptr智能指针,那么也需要将节点内的_ptr和next都定义成shared_ptr类的智能指针不然无法进行赋值给智能指针
在这里插入图片描述

这个是shared_ptr的一个延伸出来的
share_ptr 虽然已经很好用了,但是有一点 share_ptr 智能指针还是有内存泄露的情况:例如当两个 shared_ptr 相互引用时,析构时两个资源时引⽤计数会减⼀,但是两者引⽤计数还是为1,永远不可能下降为0,也就是资源永远不会释放,从而导致内存泄漏。

weak_ptr对象指向shared_ptr对象时,不会增加shared_ptr中的引用计数,因此当node1销毁掉时,则node1指向的空间就会被销毁掉,node2类似,所以weak_ptr指针可以很好解决循环引用的问题

weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象(弱引用)。进行该对象的内存管理的是那个强引用的 shared_ptr, weak_ptr 只是提供了对管理对象的一个访问手段(也就是说它只引用不计数)。weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。

在这里插入图片描述

auto p = make_shared<int>(42);

weak_ptr<int> wp(p);  //wp弱共享p,p的引用计数器未改变

if(auto np = wp.lock()){}

unique_ptr

它直接将拷贝构造函数和赋值重载函数禁用,所有不能进行拷贝和赋值

在这里插入图片描述

unique_ptr<string> p1(new string("abc"));//new 会返回一个空间的指针,然后直接构造初始化生成智能指针

unique_ptr<string> p2(p1.release());//将所有权从p1转移给p2,release将p1置为空

unique_ptr<string> p3(new string("Text"));
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值