zoukankan      html  css  js  c++  java
  • 12动态内部才能

    12.1.1 shared_ptr


    #include<memory>头文件

    使用动态内存的原因是允许多个对象共享相同的状态。
    负责自动释放所指向的对象,允许多个指针指向同一个对象。


    shared_ptr<string>p1;   //指向string的share_ptr指针
    shared_ptr<list<int>>p2; //指向int的list的share_ptr指针。
    

    一般的操作:shared_ptr和unique_ptr都支持

    shared_ptr操作
    shared_ptr<T> sp 空智能指针,可以指向类型为T的对象
    p 将p作为一个条件判断,若p指向一个对象,则为true
    *p 解引用p,获得它指向的对象
    p->men 等价于(*p).men
    p.get() 返回p中保存的指针,要小心使用,若智能指针释放了其对象,返回的指针所指向的对象也就消失了。
    swap(p,q) 交换p和q中的指针
    p.swap(q) 交换p和q中的指针

    shared_ptr特有支持的操作

    shared_ptr操作
    make_shared<T>(args) 返回一个share_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共享对象的智能指针数量;可能很慢,主要用于调试。

    shared_ptr拷贝与赋值操作

     
    shared_ptr
    引用计数器

    当shared_ptr作为参数拷贝,初始化另一个shared_ptr,作为函数的返回值,所关联的计数器就会递增。
    当我们给shared_ptr赋予一个新值,或shared_ptr被销毁,计数器就会递减。
    一旦一个shared_ptr的计数器变为0,它就会自动释放自己所管理的对象。

    shared_ptr<vector<int>> r = make_shared<vector<int>>();
    
    auto r = make_shared<int>(42);  //r指向的int只有一个引用者
    r = q;  //给r赋值,令它指向另一个地址; 递增q指向的对象的引用计数;递减r原来指向的对象的引用计数,r原来指向的对象已经没有引用者,会自动释放。
    

    关键是智能指针类能记录有多少个shared_ptr指向相同的对象,并能在恰当的时候自动释放对象。


    shared_ptr销毁对象

    shred_ptr的析构函数会递减它所指向的对象的引用计数。如果引用计数变为0,shared_ptr的析构函数就会销毁对象,并释放它占用的内存。

    void use_factory(T arg){
        shared_ptr<Foo> p = factory(arg);
        //使用p
        //p离开了作用域,它指向的内存会被自动释放
    }
    
    shared_ptr<Foo> use_factory(T arg){
        shared_ptr<Foo> p = factory(arg);
        //使用p
        return p; //当我们返回p时,引用计数进行了递增操作
        //p离开的作用域,但它指向的内存不会被释放掉。
        
    }
    
    • [当你将shared_ptr存放于一个容器中,而后不再需要全部元素,而只使用其中一部分,要记得用erase删除不再需要的那些元素。 ]

    shared_ptr与new结合使用

    shared_ptr<int> p2(new int(42)); //p2指向一个值为42的int 接受指针参数的智能指针构造函数是explicit的,所以不能进行内置指针到隐式指针的智能转换,必须使用直接初始化形式来初始化一个智能指针。 一个用来初始化智能指针的普通指针必须指向动态内存。

    shared_ptr<int> p1 = new int(1024); //错误 必须使用直接初始化方式
    shared_ptr<int> p2 (new int(1024)); //正确 使用了直接初始化方式。
    
    shared_ptr<int> clone(int p){
        return new int(p); //错误 隐式转换为shared_ptr<int>
    }
    
    shared_ptr<int> clone(int p){
        return shared_ptr<int>(new int(p)); //正确 
    }
    

    使用智能指针的规范:

    • 不使用相同的内置指针初始化(reset)多个指针。
    • 不delete get()返回的指针
    • 不使用get()初始化或reset另一个智能指针。
    • 如果使用get()返回的指针,当最后一个对应的智能指针销毁后,你的指针就变为无效了。
    • 如果你使用智能指针管理的资源不是new分配的内存,记住传递给它一个删除器。

    其他shared_ptr操作:使用reset来将一个新的指针赋予一个shared_ptr

    p.reset(new int(1024)); //p指向一个新对象
    

    与赋值类似,reset会更新计数,如果需要的话,会释放p指向的对象。

     

    12.1.2 直接管理内存new and delete


    #include<new> 头文件


    new将内存分配与对象构造组合在了一起,delete将对象析构和内存释放组合在了一起。

    int *p1 = new int ; //如果分配失败,new将抛出std::bad_alloc.
    int *p2 = new (nothrow) int;//如果分配失败,new返回一个空指针。 定位new:允许我们向new传递额外的参数。
    

    bad_alloc和nothrow都定义在头文件new中。


    delete 释放动态内存

    传给delete的指针必须是动态内存分配的,或是一个空指针,通常情况下,编译器无法判断指针指向的是静态还是动态对象。 delete也可用于const对象,虽然const对象的值不能被修改,但是其可以被销毁。 智能指针释放:最后一个shared_ptr被销毁 内置指针释放:由内置指针(而不是智能指针)管理的动态内存在被显式释放前一直都会存在。

    void use_factory(T arg){
        Foo *p  = factory(arg);
        //使用p
        delete p ; //释放内存
        
    }
    
    
    new 和 delete管理内存常见问题
    • 忘记delete内存。导致内部泄露问题,因为这种内存永远不可能被归还给自由空间了。查找内存泄露是非常困难的,通常应用程序运行很长时间后,真正耗尽内存后,才能检测到这种错误。
    • 使用已经释放掉的对象。通过在释放内存后将指针置为空,有时可以检测出这种错误。
    • 同一块内存释放两次。当有两个指针指向相同的动态分配对象时,可能发生这种错误。如果对其中一个指针进行了delete操作,对象的内存就被归还给自由空间了。如果我们随后又delete第二个指针,自由空间就会被破坏。

    解决办法:坚持使用智能指针,就可避免所有这些问题。

    12.1.3 make_shared


    #include<memory>头文件


    此函数在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr;

    shared_ptr<int> p1 = make_shared<int>(42); //指向一个值为42的int的shared_ptr;
    shared_ptr<int> p2 = make_shared<string>(10,'9');  //指向一个值为“9999999999”的string.
    shared_ptr<int> p5 = make_shared<int>(); //指向一个值初始化的int,即值为0

    通常用auto来保存make_shared的结果
    auto p6 = make_shared<vector<string>>(); //p6指向一个动态分配的空vector<string>

     

    12.1.5 unique_ptr


    #include<unique_ptr>头文件


    • 某个时刻只能有一个unique_ptr指向一个给定对象。当unique_ptr被销毁时,它所指向的对象也被销毁。
    • 当我们初始化unique_ptr时,需要将其绑定到一个new返回的指针上,初始化unique_ptr必须采用直接初始化的方式。
      -unique_ptr不支持普通的拷贝或赋值操作。

    一般的操作:shared_ptr和unique_ptr都支持

    shared_ptr操作
    shared_ptr<T> sp 空智能指针,可以指向类型为T的对象
    p 将p作为一个条件判断,若p指向一个对象,则为true
    *p 解引用p,获得它指向的对象
    p->men 等价于(*p).men
    p.get() 返回p中保存的指针,要小心使用,若智能指针释放了其对象,返回的指针所指向的对象也就消失了。
    swap(p,q) 交换p和q中的指针
    p.swap(q) 交换p和q中的指针

    unique_ptr特有支持的操作

    unique_ptr操作
    unique_ptr<T> u1 空unique_ptr,可以指向类型为T的对象。u1使用delete来释放它的指针。
    unique_ptr<T,D> u2 u2使用一个类型为D的可调用对象来释放它的指针
    unique_ptr<T,D> u(d) 空unique_ptr,指向类型为T的对象,用类型为D的对象d来代替delete.
    u = nullptr 释放u指向的对象,将u置为空
    u.release() u放弃对指针的控制权,返回指针,并将u置为空
    u.reset() 释放u指向的对象
    u.reset(q) 如果提供了内置指针q,令u指向这个对象;否则将u置为空。
    u.reset(nullptr) 否则将这个对象置为空

    调用release或reset来转移指针的所有权
    unique_ptr<string> p2(p1.resease()); //release将p1置为空  将所有权从p1转移给p2  
    
    p2.reset(p3.release()); //reset释放了p2原来指向的内存
    

    调用realse会切断unique_pre与原来所管理对象之间的关系。


    向imoqie_ptr传递删除器
    unique_ptr<objT,delT> p (new objT,fcn);// p指向一个类型为objT的对象,并且使用一个类型deLT的对象来释放objT对象。
    //它会调用一个名为fcn的delT类型对象。
    


    12.1.6 weak_ptr


    #include<memory> //头文件


    • weak_ptr是一种不控制所指向对象生存周期的智能指针,它指向由一个shared_ptr管理的对象。将一个weak_ptr绑定到shared_ptr不会改变shared_ptr的计数。
    • 一旦最后一个指向对象shared_ptr被销毁,对象就会释放。
    weak_ptr操作
    weak_ptr<T> w 空weak_ptr可以指向类型为T的对象
    weak_ptr<T> w(sp) 与shared_ptr sp指向相同对象的weak_ptr。T必须能转换为sp指向的类型。
    w = p p可以是一个shared_ptr或一个weak_ptr。赋值后w与p共享对象
    w.reset() 将w置为空
    w.ues_count() 与w共享对象的shared_ptr的数量
    w.expired() 若w.use_count()为0,返回true否则返回false
    w.lock() 如果expired()为true,返回一个空shared_ptr;否则返回一个指向w的对象的shared_ptr.

    创建一个weak_ptr

    创建一个weak_ptr,需要用shared_ptr来初始化它。

    auto p = make_shared<int>(42);
    weak_ptr<int>wp(p); //wp弱共享p;p的引用计数未改变。
    

    weak_ptr通过lock函数访问对象

    lock函数检查weak_ptr指向的对象是否存在,如果存在,则lock返回一个指向共享对象的shared_ptr。

    if(shared_ptr<int> np = wp.lock()){ //如果np不为空,则条件成立
        //在if中,np与p共对象
    }

    12.2.2  allocator 类


    #include<memory> //头文件


    当分配一大块内存时,我们通常计划在这块内存上按需构造对象,在此情况下,我们希望将内存分配和对象构造分离,这意味着我们可以分配大块内存,但只在真正需要时才真正执行对象创建操作。


    • 将内存分配与对象构造分离开来
    • 提供一种类型感知的内存分配方法
    • 分配的内存是原始的、未构造的
    • 必须指明allocator可以分配对象的类型
    • 它自动根据对象的类型来确定恰当的内存大小和对齐位置

    allocator<string>alloc; //可以分配string的allocator对象
    auto const p = alloc.allocate(n); //分配n个未初始化的string.
    

    allocator支持的操作
    allocator操作
    allocator<T> a 定义一个名为a的allocator对象
    a.allocator(n) 分配内存,保存n个类型为T的对象
    a.deallocate(p,n) 释放从T*指针p中地址开始的内存,这块内存保存了n个类型为T的对象;p必须是一个先前由allocate返回的指针,且n必须是p创建时所要求的大小。在调用deallocate之前,用户必须对每个在这块内存中创建的对象调用destroy
    a.construct(p,args) p必须是一个类型为T*的指针,指向一块原始内存;arg被传递给类型为T的构造函数,用来在p指向的内存中构造一个对象。
    a.destroy(p) p为T*类型的指针,此算法对p指向的对象执行析构函数

    allocator分配未构造的内存


    • 使用construct来构造 construct成员函数接受一个指针和零个或多个额外的参数,在给定位置构造一个元素,额外参数用来初始化构造的对象,类似于make_shared的参数,这些额外参数必须是与构造的对象的类型相匹配的合法的初始化器。
    auto p = q; //q指向最后构造的元素之后的位置
    alloc.construct(q++);       //q为空字符串
    alloc.construct(q++,10,'c');    //*q为ccccccccc
    alloc.construct(q++,"hi");  //*q为hi
    • 使用destroy来销毁 函数destroy接受一个指针,对指向的对象执行析构函数
    while(q != p)
        alloc.destroy(--q);//释放我们真正构造的string
    

    我们只能对真正构造了的元素进行destroy操作

    • 使用deallocate来释放这片内存
    alloc.deallocate(p,n);
    

    传递给deallocate的指针不能为空,它必须指向由allocae分配的内存。 传递给deallocate的大小参数必须与调用allocated分配内存时提供的大小参数具有一样的值。

    拷贝或填充未初始化内存的算法


    标准库还为allocator定义了里两个伴随算法,可以在未初始化内存中创建对象。这些函数在给定目的位置创建对象,而不是由系统分配内存给他们。

    uninitialized_copy(b,e,b2)  //从迭代器b和e指出的输入范围中拷贝元素到迭代器b2指定的未构造的原始内存中。b2执行的内存必须足够大,能容纳输入序列中元素的拷贝。
    uninitialized_cop_n(b,n,b2) //从迭代器b指向的元素开始,拷贝n个元素到b2开始的内存中。
    uninitialized_fill(b,e,t)   //在迭代器b和e指定的原始内存范围中创建对象,对象的值均为t的拷贝。
    uninitialized_fill_n(b,n,t) // 从迭代器b指向的内存地址开始创建n个对象,b必须指向足够大的未构造的院士内存,能够容纳给定数量的对象。
    
    //假定有一个int的vector,希望将其内容拷贝到动态内存中,我们将分配一个比vector中元素所占用空间大一倍的动态内存,然后将原vector中的元素拷贝到前一半空间,对后一半空间用一个给定值进行填充。
    
    auto p = alloc.allocate(vi.size()*2);//
    //通过拷贝vi中的元素来构造从p开始的元素
    auto q = uninitialized_copy(vi.begin(),vi.end(),p);
    //将剩余元素初始化为42
    uninitialized_fill_n(q,vi.size().42);
    

    与copy不同,uninitialized_copy在给定目的位置构造元素。 一次uninitialzed_copy调用会返回一个指针,指向最后一个构造的元素之后的位置。

  • 相关阅读:
    如何在Epower工作流平台(企业流程管理平台)上建立OA系统
    工作流管理系统应按需选型
    在工作流基础上的解决方案清单
    优化IT企业基础架构
    DataGrid控件用法
    Oracle设置初始化参数 REMOTE_LOGIN_PASSWORDFILE
    淘宝网的剩余时间(倒计时)实现
    Orace sequenced 简单介绍(自增长字段)
    C#与Oracle开发中执行存储过程问题
    ASP.NET中回车触发指定按钮的事件
  • 原文地址:https://www.cnblogs.com/ccpang/p/11867699.html
Copyright © 2011-2022 走看看