• C++ Primer : 第十二章 : 动态内存之shared_ptr类


    在C++中,动态内存是的管理是通过一对运算符来完成的:new  ,在动态内存中为对象分配空间并返回一个指向该对象的指针,delete接受一个动态对象的指针,销毁该对象,并释放该对象关联的内存。

    动态内存的使用很容器出现错误,确保在正确的时间释放动态内存是极其困难的。有时候我们会忘记释放动态内存,这样就会造成内存泄露;或者是一个对象还没有使用完,就释放了它所关联的内存,再去使用它,就会造成难以预估的错误。

    因此,为了更好地管理使用动态内存,C++11新标准提供了两种智能指针:shared_ptr和unique_ptr来管理动态对象。它们负责自动释放所管理的内存,shared_ptr允许多个指针同时指向同一个动态对象,unique_ptr则独占所指向的资源。标准库还定义了一个weak_ptr,它是一种弱引用,指向shared_ptr所管理的对象,但不增加shared_ptr的引用计数。三个类型都定义在 <memory>头文件中



    shared_ptr类


    智能指针也是模板,所以在创建一个shared_ptr类时,必须指出指针指向的对象的类型:
    shared_ptr<string> p1; // 指向string类型的指针
    shared_ptr<int> p2;    // 指向int类型的指针

    默认初始化的智能指针保存着一个空指针。
    下面是shared_ptr和unique_ptr都支持的操作:

    shared_ptr和unique_ptr都支持的操作
    shared_ptr<T> sp 空智能指针,可以指向类型为T的对象
    unique_ptr<T> up  
    p 将p作为一个判断条件,若p指向一个对象,则为true
    *p 解引用p,获得它指向的对象
    p->mem 等价于(*p).mem
    p.get() 返回p中保存的指针
    swap(p, q) 交换p和q中的指针
    p.swap(q)  

    下面是shared_ptr独有的操作:

    shared_ptr独有的操作
    make_shared<T>(args) 返回一个shared_ptr,指向一个动态分配的类型为T的对象。使用args初始化此对象
    shared_ptr<T>p(q) p是shared_ptr q的拷贝,此操作会递增q中的计数器,q中的指针必须能转换为T*
    p = q p和q都是shared_ptr,所保存的指针必须能相互转换,此操作会递减p的引用计数,递增q的引用计数,若p的引用计数变为0,则将其管理的原内存释放。
    p.unique() 若p.use_count()为1,返回true,否则返回false
    p.use_count() 返回与p共享对象的智能指针数量,可能很慢,主要用于调试



    • make_shared函数
    make_shared也定义在 <memory>中。 当要使用make_shared时,必须指定想要创建的对象的类型。
    // p3指向一个为1的int类型的shared_ptr
    shared_ptr<int> p3 = make_shared<int>(1); 
    
    // p4指向一个值为"9999999999"的string类型的shared_ptr
    shared_ptr<string> p3 = make_shared<string>(10, '9'); 
    
    // p5指向一个值初始化的int,即,值为0
    shared_ptr<int> p5 = make_shared<int>();
    
    

    make_shared用参数来构造给定类型的对象,例如,make_shared<string>的参数必须与string的某个构造函数的参数匹配,make_shared<int>的参数必须能初始化一个int。

    如果我们不给make_shared传递任何参数,对象就会进行值初始化。


    • shared_ptr的拷贝和赋值
    当进行拷贝或辅助时, 每个shared_ptr都会记录有多少个其他shared_ptr指向相同的对象:
    auto p = make_shared<int>(1); // p指向的对象只有p一个引用者
    auto q(p); // p和q指向相同对象,此对象有两个引用者

    每个shared_ptr都有一个关联的计数器,叫做引用计数,无论何时拷贝一个shared_ptr,计数器都会递增,包括赋值,值传递的参数传递,作为返回值;当我们赋予shared_ptr
    一个新值,或者它被销毁,比如一个shared_ptr离开所在作用域,计数器就会递减。当一个shared_ptr的计数器为0时,他就会自动释放所管理的对象。
    auto r= make_shared_ptr<int>(42); // r指向的int只有一个引用者
    r = q;    // 给r赋值,令它指向新对象
              // 递增q指向的对象的引用计数
              // 递减r指向的对象的引用计数
              // r原来指向的对象已经没有引用者,会自动释放


    • shared_ptr自动销毁所管理的对象......
    当指向一个对象的最后一个shared_ptr被销毁时,shared_ptr类会自动销毁此对象,它是通过自己的析构函数来完成销毁工作的。shared_ptr的析构函数会递减它所指向的对象的引用计数,如果引用计数变为0,shared_ptr的析构函数就会销毁对象,并释放它所占内存。
    • ......shared_ptr还会自动释放相关联的内存
    当动态对象不再被使用时,shared_ptr类会自动释放对象。例如,我们有这样一个函数,返回一个shared_ptr,指向一个Foo类型的动态分配的对象,对象是通过一个类型为T的参数进行初始化的:
    shared_ptr<Foo> factory(T arg) {
        // do some thing
        // ...
        return make_shared<Foo>(arg);
    }

    由于factory返回一个shared_ptr,所以我们可以确保它分配的对象会在恰当的时刻被释放。例如,我们可以将一个shared_ptr保存在局部变量中:
    void use_factory(T arg) {
        shared_ptr<Foo> p = factory(arg);
        // 使用p
        // p离开了作用域,它指向的内存被自动释放
    }

    p是use_factory的局部变量,在函数结束时它被销毁。在p被销毁时,会递减引用计数并检查是否为0。在此例中,p是唯一引用factory返回的内存的对象,由于p将要销毁,p指向的这个对象也被销毁,所占的内存也被释放。
    但如果有其他shared_ptr也指向这块内存,它就不会被释放:
    void use_factory(T arg) {
        shared_ptr<Foo> p = factory(arg);
        // 使用p
        return p; // 会拷贝p的一份实例,p的引用计数递增
    }


    这一次,use_factory在结束时,p被销毁,它指向的内存还有其他使用者,shared_ptr保证只要有任何shared_ptr对象引用它,它就不会被释放掉。

    如果将一个shared_ptr存放于一个容器中,而后不再需要全部元素,而只使用其中一部分,要记得erase删除不再需要的那些元素



  • 相关阅读:
    A survey of best practices for RNA-seq data analysis RNA-seq数据分析指南
    DART: a fast and accurate RNA-seq mapper with a partitioning strategy DART:使用分区策略的快速准确的RNA-seq映射器
    中科院生物信息学题目整理
    生物信息学题目整理: 陈润生
    第六章 Windows应用程序对键盘与鼠标的响应 P121 6-8
    第七章 资源在Windows编程中的应用 P157 7-8
    第四章 Windows的图形设备接口及Windows绘图 P83 4-6
    Android Fragment 你应该知道的一切
    Android Fragment 真正的完全解析(下)
    Android Fragment 真正的完全解析(上)
  • 原文地址:https://www.cnblogs.com/averson/p/5096067.html
Copyright © 2020-2023  润新知