首页 > 其他分享 >智能指针

智能指针

时间:2024-04-24 23:01:22浏览次数:27  
标签:std SharedPtr 智能 rc 拷贝 ptr 指针

首先理解什么是RAII,RAII(Resource Acquisition Is Initialization)是C++中用来管理资源的生命周期的一种技术。
在 RAII 中,资源的获取和释放是在对象的构造函数和析构函数中完成的。当对象被创建时,它的构造函数被调用,从而获取资源;当对象超出作用域时,其析构函数被调用,从而释放资源。这样就确保了资源在对象的生命周期内始终是有效的,即使在发生异常或其他错误的情况下也能正确地释放资源

不带引用计数的智能指针

下面实现一个非常简单的智能指针

#include <iostream>
#include <stdexcept>

template <typename T>
class SmartPointer
{
public:
    SmartPointer(T *ptr) : _ptr(ptr) {}
    ~SmartPointer()
    {
        delete _ptr;
    }
    T *operator->() { return _ptr; }
    T &operator*() { return *_ptr; }

private:
    T *_ptr;
};

class Bar
{
public:
    ~Bar() { std::cout << "destructor" << std::endl; }
    void func() { std::cout << "func()" << std::endl; }
};

int main()
{
    {
        SmartPointer<Bar> f(new Bar());
        f->func();
        (*f).func();
    }
}

代码的运行结果如下:
func()
func()
destructor

可以看到,这个智能指针非常简单,就是在析构函数中正确释放资源,然后提供了->*运算符重载函数。
智能指针就是对普通指针进行了一层封装,然后提供了对应的运算符重载函数,使其接口用起来像普通指针,同时在智能指针对象出作用域时会自动调用析构函数。
只要保证析构函数中,正确释放了资源,那么就能保证资源在出作用域时自动释放。

一般不将智能指针创建在堆上,创建在堆上SmartPointer<Bar> *f = new SmartPointer<Bar>(new Bar());,仍需要手动delete,才能访问智能指针的析构函数从而释放资源。这种做法与直接使用裸指针无异。

带引用计数的智能指针

上面的版本,不带引用计数,所以有一个很严重的问题。

int main()
{
    {
        SmartPointer<Bar> sp(new Bar());
        SmartPointer<Bar> sp2(sp);  // 拷贝构造
    }
}

上面这个代码片段的执行结果为:
destructor
destructor
free(): double free detected in tcache 2
Aborted (core dumped)

可以发现,程序对同一个内存执行了两次delete。这是因为,SmartPointer<Bar> sp2(sp);执行的是浅拷贝,故两个智能指针对象中存放的是同一个内存地址,所以会有double free。
那么,能不能把这个浅拷贝改成深拷贝呢?
答案显然是不能的,改成深拷贝意味着每次执行拷贝都会重新开辟内存空间,然后拷贝数据,但本来的意图可能只是需要传递一个指针而已,也就是两个指针指向同一个内存。

那么,如何解决这个问题呢?从是否共享所有权的角度来分类,有两种办法:

  1. 不共享所有权
    • 拒绝拷贝的发生,直接把拷贝(移动)构造,拷贝(移动)赋值函数删除,对应的是std::scoped_ptr
    • 每次拷贝都是移动,都把资源的所有权转移出去,也就是只有最后一个智能指针拥有所有权,其它智能指针中都置为nullptr,对应的是std::auto_ptr
    • 允许移动构造、移动赋值操作,不允许拷贝构造、拷贝赋值,也就是c++11中的std::unique_ptr,通过std::unique_ptr<T> uptr2(std::move(uptr1))来创建新对象。

std::scoped_ptr只能用于单个对象,不能拷贝移动,std::auto_ptr自动转移所有权,容易出错,比如再次使用交出所有权的对象,这俩个都不推荐使用。

std::unique_ptr可以移动构造新对象,而且是显式的发生所有权的转移的。

  1. 共享所有权
    利用引用计数,通过引用计数是否为0,来判断是否真的执行delete操作。当发生拷贝时,引用计数加一,当有一个智能指针出作用域,对应的引用计数减一,当引用计数减为0时,才真正执行delete操作。
    这样就能保证,不发生double free,同时最后一个出作用域的智能指针能够正确释放资源。

尝试实现一个带引用技术的智能指针

#include <iostream>
#include <stdexcept>

template <typename T>
class RefCnt
{

public:
    RefCnt(T *ptr) : ptr(ptr)
    {
        if (ptr)
            cnt = 1;
    }

    void increRef()
    {
        cnt++;
    }

    int decreRef()
    {
        return --cnt;
    }

private:
    T *ptr;
    int cnt; // 不是线程安全的
};

template <typename T>
class SharedPtr
{
public:
    SharedPtr(T *ptr) : ptr(ptr)
    {
        rc = new RefCnt<T>(ptr);
    }

    SharedPtr(const SharedPtr &sp) : ptr(sp.ptr), rc(sp.rc)
    {
        if (ptr)
            rc->increRef();
    }

    SharedPtr &operator=(const SharedPtr &sp)
    {
        if (this == &sp)
            return *this;

        if (0 == rc->decreRef())
        {
            delete ptr;
        }

        ptr = sp.ptr;
        rc = sp.rc;
        rc->increRef();
        return *this;
    }

    ~SharedPtr()
    {
        if (0 == rc->decreRef())
        {
            delete ptr;
            delete rc;
        }
    }

    T *operator->()
    {
        return ptr;
    }

    T &operator*()
    {
        return *ptr;
    }

private:
    T *ptr;
    RefCnt<T> *rc;
};

int main()
{
    SharedPtr<int> sp1(new int(10));
    SharedPtr<int> sp2(sp1);
    SharedPtr<int> sp3(nullptr);
    sp3 = sp2;
    *sp1 = 42;
    std::cout << *sp2 << " " << *sp3 << std::endl;
}

标签:std,SharedPtr,智能,rc,拷贝,ptr,指针
From: https://www.cnblogs.com/ericling0529/p/18156564

相关文章

  • 人工智能学习规划
    本文可谓是千呼万唤使出来,很多同学问我,AI方向的知识多而杂,哪些该重点学习?学习路径又是怎么样的呢?今天,我将自己的学习路径及我所参考的资料全部免费分享出来,愿大家的AI学习进阶之路上多一些“温度”。学习途径在我学习人工智能的过程中,主要有以下两个途径:首先是B站。因为公众......
  • 安防监控/智能分析EasyCVR视频汇聚平台海康/大华/宇视摄像头国标语音GB28181语音对讲
    一、背景分析近年来,国内视频监控应用发展迅猛,系统接入规模不断扩大,涌现了大量平台提供商,平台提供商的接入协议各不相同,终端制造商需要给每款终端维护提供各种不同平台的软件版本,造成了极大的资源浪费。各地视频大规模建设后,省级、国家级集中调阅,对重特大事件通过视频掌握现场并进......
  • 阿里云人工智能平台PAI部署stable diffusion详细步骤
    一、注册阿里云二、申请试用资格/购买算力截止时间2024/04/24依然可以申请试用,额度是三个月时间,5000算力,具体能用多久看个人三、申请文件存储服务非必须,如果没有大型模型(文件大小超过5G)需要上传,可以不申请,如需申请,白嫖的建议选择OSS而不是NAS,原因下面配置的时候会说四、......
  • 『智能方案研发』脉搏蓝牙血氧仪方案设计
    蓝牙血氧仪是一种用于测量人体血氧饱和度和脉率的设备,广泛应用于医疗、健康管理、户外运动等领域。蓝牙血氧仪以其小巧易携带、便于测量、准确直观等特点受到广泛关注。并且该医疗设备可连接米家APP,将用户测量的血氧数据上传至米家APP端,通过定时测量,APP会提醒用户在血氧低或者......
  • 指标+AI:迈向智能化,让指标应用更高效
    近日,以“Data+AI,构建新质生产力”为主题的袋鼠云春季发布会圆满落幕,大会带来了一系列“+AI”的数字化产品与最新行业沉淀,旨在将数据与AI紧密结合,打破传统的生产力边界,赋能企业实现更高质量、更高效率的数字化发展。会上,袋鼠云业务总经理申杭带来了以“指标+AI:迈向智能化,让指标应用......
  • C++ 指针变量的字面量以及其所指对象的字面量
    指针变量的字面量以及其所指对象的字面量 #include<iostream>usingnamespacestd;intmain(){intvar=20;int*var_address;var_address=&var;cout<<"Valueofvarvariavle:"<<var<<endl;//Valueofvarvari......
  • C++ 指针变量的字面量以及其所指对象的字面量
    指针变量的字面量以及其所指对象的字面量 #include<iostream>usingnamespacestd;intmain(){intvar=20;int*var_address;var_address=&var;cout<<"Valueofvarvariavle:"<<var<<endl;//Valueofvarvari......
  • C++ 指针变量的字面量以及其所指对象的字面量
    指针变量的字面量以及其所指对象的字面量 #include<iostream>usingnamespacestd;intmain(){intvar=20;int*var_address;var_address=&var;cout<<"Valueofvarvariavle:"<<var<<endl;//Valueofvarvari......
  • C++ 指针变量的字面量以及其所指对象的字面量
     指针变量的字面量以及其所指对象的字面量 #include<iostream>usingnamespacestd;intmain(){intvar=20;int*var_address;var_address=&var;cout<<"Valueofvarvariavle:"<<var<<endl;//Valueofvarv......
  • 「锐利」升级到13.5版本,重磅推出插拼式智能拼版等多项新功能!
    包装印前软件「锐利」又升级了!13.5版本!锐利是集印前行业三十多年的技术和经验开发的一款包装印前处理软件,提供陷印、拼大版、预览、智能标记、预飞、工具箱、导出图像、搜索器、油墨编辑、曲线调整、模拟套印不准、导入标准PDF、条形码、TIFF输出、无缝拼版、弯曲变形、挂网、可......