码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • shared_ptr共享的智能指针


    目录

    一、shared_ptr共享的智能指针

    1. std::shared_ptr基本用法

    1.1 std::shared_ptr初始化

    1.2 std::shared_ptr获取原始指针

    1.3 std::shared_ptr指定删除器

    2. 使用shared_ptr需要注意的问题

    2.1 不要用一个原始指针初始化多个shared_ptr

    2.2 不要子函数实参中创建shared_ptr

    2.3 通过share_from_this()返回this指针。

    2.4 要避免循环引用


    C++ 11提供了3种智能指针:std::shared_ptr、std::unique_ptr和std::weak_ptr,使用时需要引用头文件.

    一、shared_ptr共享的智能指针

    std::shared_ptr使用引用计数,每一个std::shared_ptr的拷贝都指向相同的内存。在最后一个std::shared_ptr析构事,内存在会被释放。

    1. std::shared_ptr基本用法

    1.1 std::shared_ptr初始化

    可以通过构造函数、std::make_shared 辅助函数和reset方法来初始化std::shared_ptr,代码如下:

    1. std::shared_ptr<int> p(new int(1));
    2. std::shared_ptr<int> p2=p;
    3. std::shared_ptr<int> ptr;
    4. ptr.reset(new int(1));
    5. if(ptr){
    6. cout << "ptr is not null";
    7. }

    我们应该优先使用make_shared来构造智能指针,因为它更加高效。

    不能将一个原始指针直接赋值给一个智能指针,例如,下面的这种方法是错误的:

    std::shared_ptr p = new int(1); // 编译报错,不允许直接赋值

    可以看到指针指针的用法和普通指针的用法类似,只不过不需要自己管理分配的内存。std::shared_ptr不能通过直接将原始指针赋值来初始化,需要通过构造函数和辅助方法来初始化。对于一个未初始化的智能指针,可以通过reset方法来初始化,当指针指针中有值的时候,调用reset会使引用计数减1,另外,智能指针可以通过重载bool类型操作符来判断智能指针中是否为空。

    1.2 std::shared_ptr获取原始指针

    当需要获取原始指针事,可以通过get方法来返回原始指针,代码如下:

    1. std::shared_ptr<int> ptr(new int(1));
    2. int *p2 = ptr.get();

    1.3 std::shared_ptr指定删除器

    智能指针初始化可以指定删除器,代码如下:

    void DeleteIntPtr(int *p)

    {

            delete p;

    }

    std::shared_ptr p(new int,DeleteIntPtr);

    当p的引用计数为0时,自动调用删除器DeleteIntPtr来释放对象的内内存。删除器可以是一个lambda表达式,因此,上面的写法还可以为:

    std::shared_ptr<int> p(new int,[](int p*){delete p;});
    

    当我们用std::shared_ptr管理动态数组时,需要指定删除器,因为std::shared_ptr的默认删除不支持数组对象,代码如下:

    std::shared_ptr<int> p(new int[10]),std::default_delete<int []>);
    

    另外,还可以通过封装一个make_shared_array方法来让std::shared_ptr支持数组,代码如下:

    1. template<typename T>
    2. shared_ptr make_shared_array(size_t size)
    3. {
    4. return shared_ptr(new T[size],default_delete());
    5. }

    测试代码

    1. std::shared_ptr<int> p = make_shared_array<int>(10);
    2. std::shared_ptr<char> p = make_shared_array<char>(10);

    2. 使用shared_ptr需要注意的问题

    智能指针虽然自动管理堆内存,但是他还是有不善陷阱,在使用时需要注意。

    2.1 不要用一个原始指针初始化多个shared_ptr

    例如下面就是错误的:

    1. int *ptr = new int;
    2. std::shared_ptr<int> p1(ptr);
    3. std::shared_ptr<int> p2(ptr); // logic err

    2.2 不要子函数实参中创建shared_ptr

    下面为错误例子

    function (shared_ptr<int> (new int) ,get());
    

    因为C++函数参数的计算顺序在不同的编译器不同的调用约定下可能是不一样的,一般是从右到左,但也有可能是从左到右,所以可能的过程是先new int,然后调用,如果恰好g()发生异常,而share_ptr 还没有创建,则内存泄露了,争取的写法应该是先创建之智能指针,代码如下:

    1. share_ptr p(new int());
    2. f(p,g());

    2.3 通过share_from_this()返回this指针。

    不要将this指针作为share_ptr返回出来,因为this指针本质上市一个裸指针,因此,这样可能会导致重复析构,看下面例子。

    1. struct A{
    2. shared_ptr GetSelf(){
    3. return share_ptr(this); // 千万不要这样做!!!!
    4. }
    5. };
    6. int main(){
    7. shared_ptr sp1(new A);
    8. shared_ptr sp2 = sp->GetSelf();
    9. return 0;
    10. }

    正确写法,正确返回this的shared_ptr的做法是:让目标类通过派生std::endable_shared_from_this类,然后使用基类的成员变量shared_from_this来返回this的share_ptr,看下面示例。

    1. class A :public std::enable_shared_from_this
    2. {
    3. std::shared_ptr GetSelf(){
    4. return shared_from_this();
    5. }
    6. };
    7. std::shared_ptr spy(new A);
    8. std::shared_ptr p = spy->GetSelf();

    2.4 要避免循环引用

    稚嫩指针最大的一个陷阱是循环引用,循环引用会导致内存泄露。

    下面是一个经典的循环引用的场景。

    1. strcut A;
    2. strcut B;
    3. strcut A{
    4. std::shared_ptr bptr;
    5. ~A(){cout << "A is deleted !";}
    6. };
    7. strcut B{
    8. std::shared_ptr aptr;
    9. ~A(){cout << "B is deleted !";}
    10. };
    11. void TestPtr(){
    12. {
    13. std::shared_ptr ap(new A);
    14. std::shared_ptr bp(new B);
    15. ap->bptr = bp;
    16. bp->aptr = ap;
    17. }
    18. }

    测试结果是两个指针A和B都不会被删除,存在内存泄露。循环引用导致ap和bp的引用计数为2,在离开作用域后,ap和bp的引用计数减少为1,并不会减为0,导致两个指针不会被析构,产生内存泄露。解决办法是把A和B任何一成员变量改为weak_ptr.

  • 相关阅读:
    照片处理软件 DxO FilmPack 7 mac中文版软件介绍
    1671 得到山行数组的最少删除次数(贪心+二分)
    Gateway的服务网关
    react笔记_06react中进行事件绑定
    uniapp开发小程序接入阿里云TTS语音合成(RESTful API)
    linux三种软件安装方式
    小程序毕设作品之微信汽车维修保养小程序毕业设计成品(3)后台功能
    create® 3入门教程-按钮和光环与用户进行交互
    腾讯云服务器CVM_云主机_云计算服务器_弹性云服务器
    MySQL的增删查改 初阶+进阶
  • 原文地址:https://blog.csdn.net/arv002/article/details/125397345
  • 最新文章
  • 沪漂五周年了:我越来越迷茫了
    Agentic Skill Routing 实战:别再把所有 Skill 塞进 AI Agent 上下文
    MySQL-Seconds_behind_master的精度误差
    [MAF预定义ChatClient中间件-03]CachingChatClient——利用缓存省钱省时间
    AI的至暗历史:从万众期待到被政府撤资,AI的两次死亡徘徊
    Agent OS :五种驯服不确定性的范式
    PortSwigger SQL注入LAB11
    数据库即时编译JIT
    [Begin]AI Learn Data Day 0
    深度学习进阶(二十七)现代 LLM 的核心架构设计其二:SwiGLU
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
小工具 小游戏
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1

京公网安备 11010502049817号