C++智能指针shared_ptr用法

这篇具有很好参考价值的文章主要介绍了C++智能指针shared_ptr用法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


写在前面的总结:
一个shared_ptr对象管理一个指针(new T,在堆空间),多个shared_ptr对象可以管理同一个指针,只有某个shared_ptr对象第一次初始化指针时才执行指针的构造函数,管理同一个指针的shared_ptr对象个数称为引用计数,这个引用计数保存在每个管理该指针的shared_ptr对象中,当引用计数为0时,这个指针执行析构函数释放;shared_ptr对象也可以管理空指针,此时引用计数为0。
shared_ptr做为函数参数传递时,函数运行期间引用计数加一,函数运行完后离开作用域,引用计数减一。

shared_ptr功能介绍

智能指针和普通指针用法相似,智能指针的本质是一个模板类,对普通指针进行了封装,通过在构造函数中初始化分配内存,在析构函数中释放内存,达到自己管理内存,不需要手动管理内存的效果,避免了忘记释放内存而导致的内存泄露。

shared_ptr 是C++11提供的一种智能指针类,可以在任何地方都不使用时自动删除相关指针,从而帮助彻底消除内存泄漏和悬空指针的问题。
它遵循共享所有权的概念,即不同的 shared_ptr 对象可以与相同的指针相关联,并在内部使用引用计数机制来实现这一点。
每个 shared_ptr 对象在内部指向两个内存位置:
1、指向对象的指针。
2、用于控制引用计数数据的指针。
共享所有权如何在参考计数的帮助下工作:
1、当新的 shared_ptr 对象与指针关联时,则在其构造函数中,将与此指针关联的引用计数增加1。
2、当任何 shared_ptr 对象超出作用域时,则在其析构函数中,它将关联指针的引用计数减1。如果引用计数变为0,则表示没有其他 shared_ptr 对象与此内存关联,在这种情况下,它使用delete函数删除该内存。

shared_ptr是以类模板的方式实现的,shared_ptr(其中 T 表示指针指向的具体数据类型)的定义位于头文件。

shared_ptr提供的接口

shared_ptr是通过引用计数的方式来实现多个shared_ptr对象之间共享资源。
shared_ptr的存储指针和引用计数指针是一一对应的,即shared_ptr里存的是存储指针,对应的引用计数指针就是对stored pointer的加一,因此shared_ptr在其内部,给每个资源都维护着一份计数,用来记录该份资源被几个对象共享。
C++智能指针shared_ptr用法

shared_ptr初始化

构造函数初始化

///1.构造函数初始化
//传入空指针或什么都不传,构造出空智能指针,其初始引用计数方式为0
std::shared_ptr<int>  p0(nullptr);
printf("p0.use_count=%ld\n",p0.use_count());//p0.use_count=0

//构造函数初始化,指向一个存有5这个int类型数据的堆内存空间
std::shared_ptr<int> p1(new int(5));
printf("p1=%p\n",&p1);//p1=0x7ffc24046f10
printf("p1=%d\n",*p1);//p1=5
printf("p1.use_count=%ld\n",p1.use_count());//1

std::shared_ptr<int> p2(p1);//p1和p2都指向那个存有int型5的堆内存空间,堆内存的引用次数会加1
printf("p1.use_count=%ld\n",p1.use_count());//2
printf("p2.use_count=%ld\n",p2.use_count());//2

std::shared_ptr<int> p3=p0;//P0为空,则P3也为空,其引用计数依然为0
printf("p3.use_count=%ld\n",p3.use_count());//0

//补充
//可以把原始指针传参构造shared_ptr对象,此时原始指针没有new或没有初始化赋值,use_count都是1
int *p11 = new int;//如果没有=new int,下面p12.use_count还是1,但执行打印时会crash
//p11 = nullptr;//如果p11赋值为nullptr,下面p12.use_count还是1,但执行打印时会crash
std::shared_ptr<int> p12(p11);
printf("p12.use_count=%ld\n",p12.use_count());//1
printf("*p12=%d\n",*p12);//*p12=1345903632(原始指针new了,但没有初始化,则打印未知值)

std::make_shared 初始化

///2.std::make_shared 初始化
std::shared_ptr<int> p4 = std::make_shared<int>(); //1.定义一个空的智能指针
std::shared_ptr<int> p5= std::make_shared<int>(10);//2.创建指针,并明确指向
auto p6 = std::make_shared<std::vector<int>>();//3.auto关键字代替std::shared_ptr,p8指向一个动态分配的空vector<int>

reset初始化

///3.reset初始化
//调用reset(new xxx())重新赋值时,智能指针首先是生成新对象,然后将旧对象的引用计数减1(当然,如果发现引用计数为0时,则析构旧对象),然后将新对象的指针交给智能指针保管。
std::shared_ptr<int> p7(new int(20));
std::shared_ptr<int> p8(p7);
std::shared_ptr<int> p9(p7);
printf("p9.use_count=%ld\n",p8.use_count());//3
p7.reset(new int(21));//当为reset传递一个新申请的堆内存时,则调用该函数的 shared_ptr 对象会获得该存储空间的所有权,并且引用计数的初始值为1;其所指向的原堆内存引用计数减1
printf("p9.use_count=%ld\n",p8.use_count());//2
p9.reset();//当reset没有实参时,该函数会使当前 shared_ptr 所指堆内存的引用计数减 1,同时将当前对象重置为一个空指针
if(p9 == nullptr)
    printf("p10 == nullptr\n");//p10 == nullptr
printf("p9.use_count=%ld\n",p8.use_count());//1

shared_ptr管理指针的构造和析构

下面例子介绍shared_ptr是如何管理所指向指针(堆空间)的构造和析构的。

class book
{
public:
    book(int v) {//构造函数
        value = v;
        std::cout << "cons book value=" <<value<< std::endl;
    }
    ~book() {//析构函数
        std::cout << "desc book value=" <<value<< std::endl;
    }
    int value;
};

{
    std::shared_ptr<book> b1(new book(100));//cons book value=100
    std::shared_ptr<book> b2(b1);//只增加了引用计数,没有新增构造book,use_count=2
    b2.reset(new book(200));//cons book value=200(新构造book200,原book100计数变为1)
    b1.reset(new book(300));//cons book value=300   desc book value=100(新构造book300,原book100引用计数变为0,执行析构)

    printf("end\n");//准备离开作用域
}

打印,shared_ptr初始化时如果生成了新的指针,则执行指针的构造函数,如果指向该指针的shared_ptr对象引用计数为0时,执行该指针的析构函数;
如果shared_ptr对象超出了作用域,则引用计数减1,引用计数为0时执行析构。

cons book value=100
cons book value=200
cons book value=300
desc book value=100
end
desc book value=200
desc book value=300

shared_ptr获取原始指针

智能指针提供了get()成员函数,用来执行显示转换,返回智能指针内部的原始指针。

std::shared_ptr<int> p10(new int(300));
int *pn = p10.get();
printf("pn=%d\n",*pn);//pn=300

shared_ptr的线程安全

1、shared_ptr不是线程安全的;
2、在多线程下,不能保证new出来一个对象一定能被放入shared_ptr中,也不能保证智能指针管理的引用计数的正确性;
3、同一个shared_ptr对象可以被多线程同时读取,不同的shared_ptr对象可以被多线程同时修改,但同一个shared_ptr对象不能被多线程直接修改;
4、在创建一个shared_ptr时,需要使用C++11提供的make_shared模板,make_shared创建shared_ptr只申请一次内存,避免了上述错误,也提高了性能,同时在读写操作时,需要加锁。

shared_ptr应用之enable_shared_from_this

C++11 开始支持 enable_shared_from_this,它是一个模板类,定义在头文件 ,其原型为:

template< class T > class enable_shared_from_this;

enable_shared_from_this 能让一个指针(假设其名为 t ,且已被一个 std::shared_ptr 对象 pt 管理)安全地生成其他额外的 std::shared_ptr 实例(假设名为 pt1, pt2, … ) ,它们与 pt 共享对象 t 的所有权。
若一个类 T 继承 std::enable_shared_from_this ,则会为该类 T 提供成员函数: shared_from_this 。
当 T 类型对象 t 被一个为名为 pt 的 std::shared_ptr 类对象管理时,调用 T::shared_from_this 成员函数,将会返回一个新的 std::shared_ptr 对象,它与 pt 共享 t 的所有权。

使用场景
1、当类A被share_ptr管理,且在类A的成员函数里需要把当前类对象作为参数传给其他函数时,就需要传递一个指向自身的share_ptr。
2、在异步调用中,可能会使用到之前存在的变量,为了保证该变量在异步调用中一直有效,可以传递一个指向自身的share_ptr给异步函数,这样share_ptr所管理的对象就不会析构(引用计数至少>=1,保活)。

#include <iostream>
class Good : public std::enable_shared_from_this<Good> // 注意:继承
{
public:
    Good(){std::cout << "Good::Good() called" << std::endl; }
    std::shared_ptr<Good> getptr() {
        return shared_from_this();
    }
    ~Good() { std::cout << "Good::~Good() called" << std::endl; }
};
MainWindow::MainWindow(QWidget *parent)    : QMainWindow(parent)    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    std::shared_ptr<Good> gp1(new Good());
    std::cout << "gp1.use_count() = " << gp1.use_count() << std::endl;//1
    std::shared_ptr<Good> gp2 = gp1->getptr();
    std::cout << "gp1.use_count() = " << gp1.use_count() << std::endl;//2
}

打印

Good::Good() called
gp1.use_count() = 1
gp1.use_count() = 2
Good::~Good() called

weak_ptr

std::weak_ptr 是一种智能指针,通常不单独使用,只能和 shared_ptr 类型指针搭配使用,可以视为 shared_ptr 指针的一种辅助工具。借助 weak_ptr 类型指针可以获取 shared_ptr 指针的一些状态信息,比如有多少指向相同的 shared_ptr 指针、通过expired()判断shared_ptr 指针指向的堆内存是否已经被释放等等,还可以解决shared_ptr 循环引用的问题。

weak_ptr可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。使用weak_ptr的成员函数use_count()可以观测资源的引用计数,另一个成员函数expired()的功能等价于use_count()==0,但更快。表示被观测的资源(也就是shared_ptr的管理的资源)已经不复存在。

lock()从被观测的shared_ptr获得一个可用的shared_ptr对象, 从而操作资源。但当expired()==true的时候,lock()函数将返回一个存储空指针的shared_ptr。

        std::shared_ptr<int> sh_ptr = std::make_shared<int>(10);//创建一个智能指针
        printf("sh_ptr.use_count()=%ld\n",sh_ptr.use_count());//sh_ptr.use_count()=1

        std::weak_ptr<int> wp(sh_ptr);//构造 weak_ptr,不会增加智能指针的引用计数
        printf("sh_ptr.use_count()=%ld\n",sh_ptr.use_count());//sh_ptr.use_count()=1
        printf("wp.use_count() =%ld\n",wp.use_count() );// wp.use_count() =1

        if(!wp.expired())
        { // 检查sh_ptr是否还有效
            std::shared_ptr<int> sh_ptr2 = wp.lock(); //使用 weak_ptr 构造一个智能指针,引用计数+1
            *sh_ptr = 100;
            printf("wp.use_count() =%ld\n",wp.use_count() );// wp.use_count() =2
        }
    } //delete memory

    std::weak_ptr<int> wp;
    {
        std::shared_ptr<int> sh_ptr = std::make_shared<int>(10);
        wp = sh_ptr;//构造 weak_ptr
        printf("wp.expired()=%d\n",wp.expired());// wp.expired()=0,引用对象还没删除
    } //delete memory

    printf("wp.expired()=%d\n",wp.expired()); // wp.expired()=1,引用对象已经删除

sh_ptr.use_count()=1
sh_ptr.use_count()=1
wp.use_count() =1
wp.use_count() =2
wp.expired()=0
wp.expired()=1

循环引用问题
weak_ptr的一个作用是解决share_ptr的循环引用问题。如下面代码所示,class AA中含有指向class BB的shared指针, class BB 中含有指向class AA的shared指针,这样形成了循环引用。m_bb_ptr和m_aa_ptr的强引用计数永远大于等于1

class BB;

class AA
{
public:
    AA() { printf("AA::AA() called\n"); }
    ~AA() { printf( "AA::~AA() called\n"); }
    std::shared_ptr<BB> m_bb_ptr;//正确用法是使用weak_ptr,可以正常析构
};

class BB
{
 public:
    BB() { printf("BB::BB() called\n" ); }
    ~BB() { printf("BB::~BB() called\n" ); }
    std::shared_ptr<AA> m_aa_ptr;//正确用法是使用weak_ptr,可以正常析构
};

    std::shared_ptr<AA> ptr_a(new AA);
    std::shared_ptr<BB> ptr_b(new BB);
    printf( "ptr_a use_count: %ld\n" , ptr_a.use_count() );
    printf( "ptr_b use_count: %ld\n" , ptr_b.use_count() );
    //下面两句导致了AA与BB的循环引用,结果就是AA和BB对象都不会析构
    ptr_a->m_bb_ptr = ptr_b;
    ptr_b->m_aa_ptr = ptr_a;
    printf( "ptr_a use_count: %ld\n" ,ptr_a.use_count() );
    printf( "ptr_b use_count: %ld\n" ,ptr_b.use_count() );

AA::AA() called
BB::BB() called
ptr_a use_count: 1
ptr_b use_count: 1
ptr_a use_count: 2
ptr_b use_count: 2

智能指针shared_ptr开发注意事项

均为代码实测记录。

基本使用
1、std::shared_ptr p1调用reset后use_count减1,p1 == nullptr。
2、一个智能指针std::shared_ptr p1引用计数为0或者是空指针,则p1 == nullptr,不能调用类接口,但可以调用p1.use_count(),返回0。
3、不能使用delete释放std::shared_ptr的.get原始指针,程序会crash。
4、std::shared_ptr被push_back到std::vector数组,use_count加1,如果数组clear或智能指针成员被erase,use_count减1。

shared_from_this
1、类继承于std::enable_shared_from_this<>后,可以定义接口返回shared_from_this,这样函数就可以传递.get原始指针,在函数体内调用接口返回shared_from_this,在函数体内use_count加1。
2、如果使用shared_from_this().get()传参给void*,在函数执行过程中use_count加1,执行完成后use_count减1。
3、如果智能指针已释放,原始指针失效,此时通过原始指针调用接口返回shared_from_this时会抛出异常,可以捕获异常:try {pUdpSptr = pUdp->get_shared_ptr();} catch (std::exception &ex) {LogError ( " %s", ex.what()); }。

智能指针传参
1、std::shared_ptr可以通过.get获取原始指针,传参给void*类型,但引用计数不会增加。
2、std::shared_ptr p1做为普通参数传递,如果p1是空指针,则传递过程中use_count始终为0;如果p1不是空指针,执行过程中use_count加1,执行完成后use_count减1。
3、std::shared_ptr做为引用传递,如果p1是空指针,则传递过程中use_count始终为0;如果p1不是空指针,函数执行前后use_count也不会改变。
4、std::shared_ptr传参,如果要改变值,可以传shared_from_this,做为普通参数传递,执行过程中use_count加1,增加安全行,也可以改变原始指针的值。
5、lambda表达式传参智能指针,值传递会增加引用计数,引用传递不会增加引用计数;值传递,lambda表达生成的匿名函数func=nullptr后,引用计数减一。

智能指针做为返回值
1、std::shared_ptr做为普通返回值,use_count加1,但是如果没用定义std::shared_ptr变量接收返回值,use_count不变。
2、返回std::shared_ptr的引用,use_count和普通返回值一样;不能返回局部变量或临时变量的引用。
3、如果要返回空智能指针,可以return std::shared_ptr<>(nullptr)。

智能指针的构造和析构
1、新增std::shared_ptr变量时,如果生成了新的原始指针,则执行构造函数。
2、新增std::shared_ptr变量时,如果只增加了引用计数,没有生成新指针,则不会执行构造函数。
3、当引用计数变为0时,执行析构函数。
4、一句话,use_count从0变为1时执行构造,use_count从1变为0时执行析构。文章来源地址https://www.toymoban.com/news/detail-437141.html

到了这里,关于C++智能指针shared_ptr用法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++入门到精通】智能指针 shared_ptr 简介及C++模拟实现 [ C++入门 ]

    在 C++ 动态内存管理中,除了 auto_ptr 和 unique_ptr 之外,还有一种 智能指针 shared_ptr ,它可以让多个指针共享同一个动态资源,并且能够自动释放资源。 shared_ptr 通过引用计数的方式来管理内存,能够避免程序中出现悬空指针和内存泄漏等问题 。本文将介绍 shared_ptr 的简介和

    2024年01月22日
    浏览(38)
  • 智能指针shared_ptr

    shared_ptr共享它指向的对象,内部采用计数机制来实现。当新的shared_ptr与对象关联时,引用计数加1;当shared_ptr超出作用域时,引用计数减1;当引用计数为0时,释放该对象; shared_ptrA p0 = std::make_sharedA(\\\"西红柿\\\");//C++11提供 重载了*和-操作符,可以像使用指针一样使用shared_ptr

    2023年04月23日
    浏览(31)
  • C++11中的智能指针unique_ptr、shared_ptr和weak_ptr详解

    目录 1、引言 2、什么是智能指针? 3、在Visual Studio中查看智能指针的源码实现 4、独占式指针unique_ptr 4.1、查看unique_ptr的源码实现片段 4.2、为什么unique_ptr的拷贝构造函数和复制函数被delete了?(面试题) 4.3、使用unique_ptr独占式智能指针的实例 5、共享式指针shared_ptr  5.1、查

    2024年02月08日
    浏览(30)
  • C++11 新特性 ⑥ | 智能指针 unique_ptr、shared_ptr 和 weak_ptr

    目录 1、引言 2、unique_ptr 3、shared_ptr 4、weak_ptr 5、shared_ptr循环引用问题(面试题)

    2024年02月09日
    浏览(32)
  • 深入理解和应用C++ std::shared_ptr别名构造函数

    在现代C++中,智能指针是一个极为重要的工具,尤其std::shared_ptr以其自动内存管理、引用计数和多线程安全性等特性深受开发者喜爱。其中一个不太常用但功能强大的构造方式是 别名构造函数 ,它允许我们创建一个共享相同底层对象但是指向其内部不同数据成员或子对象的

    2024年01月16日
    浏览(35)
  • shared_ptr和unique_ptr主动释放

    shared_ptr和unique_ptr均可以采用reset()来进行释放,unique_ptr调用了reset之后就会直接释放掉,shared_ptr则会在所有引用计数变为0的时候才会释放申请的内存。 注意unique_ptr的release()方法,并不会释放资源,只会把unique_ptr置为空指针,原来那个资源可以继续调用 reset 结果: 在reset之

    2024年02月14日
    浏览(28)
  • 论 shared_ptr的线程安全

    今天有同事问我shared_ptr是线程更安全的吗?我当时脑子一懵,有点不确定。 但回过神来仔细一想这什么鸟问题,c++ stl里有线程安全的吗,shared_ptr 也不是针对线程安全而设计出来的呀,八竿子打不着的东西为什么会凑在一起问。 好像也就一个atmoic引用计数可以沾上边。 首先

    2024年02月08日
    浏览(28)
  • 面试之快速学习C++11-完美转发,nullptr, shared_ptr,unique_ptr,weak_ptr,shared_from_this

    函数模版可以将自己的参数完美地转发给内部调用的其他函数。所谓完美,即不仅能准确地转发参数的值,还能保证被转发参数的左右值属性不变 引用折叠:如果任一引用为左值引用,则结果为左值引用,否则为右值引用。 上述 T 为int 。 那么整个为 int - int 回到完美转发,

    2024年02月12日
    浏览(24)
  • 【C++入门到精通】智能指针 auto_ptr、unique_ptr简介及C++模拟实现 [ C++入门 ]

    在 C++ 中,智能指针是一种非常重要的概念,它能够帮助我们自动管理动态分配的内存,避免出现内存泄漏等问题。在上一篇文章中,我们了解了智能指针的基本概念和原理, 本篇文章将继续介绍 auto_ptr 和 unique_ptr 两种智能指针的概念及其在 C++ 中的模拟实现 。通过学习这些

    2024年01月19日
    浏览(53)
  • 【C++】auto_ptr为何被唾弃?以及其他智能指针的学习

    搭配异常可以让异常的代码更简洁 文章目录 智能指针     内存泄漏的危害     1.auto_ptr(非常不建议使用)     2.unique_ptr     3.shared_ptr     4.weak_ptr 总结 C++中为什么会需要智能指针呢?下面我们看一下样例:  在上面的代码中,一旦出现异常那就会造成内存泄漏,什么是内存

    2024年02月11日
    浏览(23)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包