zoukankan      html  css  js  c++  java
  • 内存管理之智能指针shared_ptr

    智能指针(smart pointer)


    智能指针的行为类似常规指针,重要的区别在于智能指针负责自动释放所指向的对象,头文件memory
    shared_ptr : 允许多个智能指针指向同一个对象
    unique_ptr : 一个指针“独占”所指向的对象
    weak_ptr : 弱引用,指向shared_ptr所管理的对象
     
     
    shared_ptr:

    1.创建一个shared_ptr,必须提供指针指向的类型,命名格式:shared_ptr<类型> 指针名;
    例如:shared_ptr<string> sptr 或 shared_ptr<vector<int>> vctptr,上述形式的初始化,返回的智能指针中保存一个空指针
     
    2.与普通指针相似,解引用(*)智能指针返回它指向的对象
     
    3.shared_ptr支持的操作,参看c++ primer 5 Edition P401
     
    4.make_shared函数:作为智能指针的初始化方式之一,安全的分配和使用动态内存。功能:在动态内存中,分配一个对象并初始化,返回此对象的shared_ptr,
    格式:shared_ptr<类型> 指针名 = make_shared<类型>();例如:
    shared_ptr<int> iptr = make_ptr<int>(); // 使用int的默认构造函数,初始化shared_ptr对象的内容
    shared_ptr<int> iptr = make_ptr<int>(36); // 使用int的重载构造函数,初始化shared_ptr对象的内容为36

    5.shared_ptr的拷贝和赋值

    当拷贝和赋值操作时,每个shared_ptr都会记录有多少个其他的shared_ptr指向相同的对象,即:每个shared_ptr都有一个关联的计数器,引用计数;无论何时拷贝(赋值)一个shared_ptr,计数器都会递增。
     
    递增的情况有三种(这里类似复制构造函数,存在的情形):
    a.用一个shared_ptr初始化另外一个shared_ptr
    b.作为函数参数传参(拷贝实参对象)
    c.作为函数返回值(返回出来还是要赋值)
     
    指向同一对象的所有shared_ptr指针的计数都相同,同时更新(+1或-1),一旦计数器变为0,则会自动释放shared_ptr所管理的对象
     
    智能指针初始化方式:
    shared_ptr<int> iptr = make_shared<int>(5);
    cout << iptr.use_count() << endl;
    shared_ptr<int> data = make_shared<int>(1); // 初始化方式1:make_shared
    // shared_ptr<int> data(iptr); 初始化方式2:直接初始化
    iptr = data; // 初始化方式3:赋值:智能指针之间相互赋值
    cout << *iptr << endl;
    cout << data.use_count() << endl;
    智能指针只能用只能指针来初始化,或者相互赋值;不能用内置类型指针来赋值,可以用内置类型指针直接初始化
    6.shared_ptr自动销毁管理对象,也会自动释放相关联的动态内存
     
    指向一个对象的最后一个shared_ptr销毁时,shared_ptr类会自动销毁此对象,该类中有析构函数,shared_ptr的析构函数会自动递减它所指向的对象的引用计数,当变为0时,shared_ptr的析构函数就会销毁对象,并释放占用的内存。
    对于一块内存,shared_ptr类保证只要有任何shared_ptr对象引用它,就不会被释放掉
     
    例子1:
    // func返回一个shared_ptr,指向一个动态分配的对象
    shared_ptr<foo> func(T arg) {
        return make_shared<foo>(arg);
    }
    foo类,使用T类型参数arg,通过构造函数初始化一块内存,nake_shared函数将该内存地址返回给智能指针shared_ptr,
    并作为函数func的返回值,返回一个shared_ptr,可以确保在函数体内申请的动态内存会在恰当的时刻被释放
    例子2:
    void func2(T arg) {
        shared_ptr<foo> p = make_shared<foo>(arg);
       // 使用p, 当p离开作用域,它指向的内存会被自动释放
    }
    例子3:
    shared_ptr<foo> func3(T arg) {
        shared_ptr<foo> p = make_shared<foo>(arg);
        return p;  
        // 使用p,当返回p时,引用计数进行了递增操作,当p离开作用域,它指向的内存会被自动释放
    }
    return语句向此函数的调用者返回一个P的拷贝。拷贝一个shared_ptr会增加所管理的对象的引用计数值;当p销毁时,该内存还有其他使用者,内存就不会被释放

    如果忘记了销毁程序不再需要的shared_ptr,程序仍然会正常运行,但是会浪费内存。shared_ptr在无用之后仍然保留的一种可能情况:shared_ptr放在容器中,然后重排了容器,从而不再需要某些元素,在这种情况下,应该erase删除不需要的shared_ptr指针。

     
    7.shared_ptr一些初始化操作:直接内存管理(参考内存管理---直接内存管理)
    前面5提到过shared_ptr的初始化,总结一下:
    1.不进行初始化,会自动初始化为一个空指针nullptr
    shared<类型> 指针名;例如:shared_ptr<int> pi;  
    2.使用内置类型指针直接初始化
    shared_ptr<类型> 指针名(内置类型指针p); 这里的p --> 类型* p = new 类型()
    shared_ptr<类型> 指针名(new<类型>())// shared_ptr与new结合使用
    3.shared_ptr<类型> 指针名(智能指针ptr); // 使用智能指针直接初始化
    4.shared_ptr<类型> 指针名 = 智能指针ptr; //智能指针赋值初始化
    5.shared_ptr<类型> 指针名 = make_shared<类型>(); // make_shared函数初始化
    不能将一个内置指针隐式的转换成一个智能指针,必须使用直接初始化的形式
    一个用来初始化智能指针的普通指针必须指向动态内存,因为智能指针默认使用delete释放它所关联的对象
     
    8.定义改变shared_ptr的其他方法  参看c++ primer 5 Edition P412-413
     
    9.不要混合使用普通指针和智能指针
    当一个shared_ptr绑定到一个普通指针时,就将内存的管理责任交给了shared_ptr
     
    10.不要使用智能指针get()方法初始化另一个智能指针或者智能指针赋值
    get()方法可以获取该智能指针的对应的内置指针,该内置指针指向智能指针管理的对象
    使用get()返回的指针代码不能delete此指针,因为该返回的内置指针指向智能指针管理的对象,该对象的释放由智能指针来管理
     
    11.智能指针和异常:智能指针可以保证当程序发生异常时,仍旧能正确的释放资源
  • 相关阅读:
    leetcode 33. Search in Rotated Sorted Array
    leetcode 32. Longest Valid Parentheses
    leetcode 28. Implement strStr()
    leetcode 27. Remove Element
    leetcode 26. Remove Duplicates from Sorted Array
    leetcode 24. Swap Nodes in Pairs
    leetcode 22. Generate Parentheses
    树莓派的频率管理和热控制
    sql执行insert插入一条记录同时获取刚插入的id
    全程直播个人博客重构过程,采用springboot+dubbo+jpa技术栈。
  • 原文地址:https://www.cnblogs.com/zhang716921/p/10626709.html
Copyright © 2011-2022 走看看