zoukankan      html  css  js  c++  java
  • CPP(c++) 智能指针

    智能指针

    参考:https://blog.csdn.net/flowing_wind/article/details/81301001

    我们知道除了静态内存和栈内存外,每个程序还有一个内存池,这部分内存被称为自由空间或者堆。程序用堆来存储动态分配的对象即那些在程序运行时分配的对象,当动态对象不再使用时,我们的代码必须显式的销毁它们。
    在C++中,动态内存的管理是用一对运算符完成的:new和delete,new:在动态内存中为对象分配一块空间并返回一个指向该对象的指针,delete:指向一个动态独享的指针,销毁对象,并释放与之关联的内存。
    动态内存管理经常会出现两种问题:一种是忘记释放内存,会造成内存泄漏;一种是尚有指针引用内存的情况下就释放了它,就会产生引用非法内存的指针。
    为了更加容易(更加安全)的使用动态内存,引入了智能指针的概念。智能指针的行为类似常规指针,重要的区别是它负责自动释放所指向的对象。标准库提供的两种智能指针的区别在于管理底层指针的方法不同,
    shared_ptr允许多个指针指向同一个对象,unique_ptr则“独占”所指向的对象。标准库还定义了一种名为weak_ptr的伴随类,它是一种弱引用,指向shared_ptr所管理的对象,这三种智能指针都定义在memory头文件中。


    常用智能指针:shared_ptr,unique_ptr和weak_ptr
    shared_ptr允许多个指针指向同一个对象,unique_ptr则“独占”所指向的对象。标准库还定义了一种名为weak_ptr的伴随类,它是一种弱引用,指向shared_ptr所管理的对象,这三种智能指针都定义在memory头文件中。

    shared_ptr<int> p3 = make_shared<int>(42);
    shared_ptr<string> p4 = make_shared<string>(10,'9');
    shared_ptr<int> p5 = make_shared<int>();
    
    //可以通过调用release或reset将指针所有权从一个(非const)unique_ptr转移给另一个unique 
    //将所有权从p1(指向string Stegosaurus)转移给p2
    unique_ptr<string> p2(p1.release());//release将p1置为空
    unique_ptr<string> p3(new string("Trex"));
    //将所有权从p3转移到p2
    p2.reset(p3.release());//reset释放了p2原来指向的内存

    shared_ptr和unique_ptr用法:

    shared_ptr<T> sp;  //空指针,可以执行类型为T 的对象
    unique_ptr<T> up;    
    p                  //p看做一个条件判断,若p指向一个对象,则为true
    *p                 //获取它指向的对象
    p->mem             //等价于(*p).mem
    p.get()            //返回P中保存的指针。
    swap(p,q)          //交换p和q中的指针
    p.swap(q)   

    share_ptr:

    make_share<T> (args)  //返回一个shared_ptr,指向一个动态分配的类型为T的对象,使用args初始化。
    share_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共享对象的智能指针数量;可能很慢,主要用于调试。

    unique_ptr: 

    //由于一个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)

    weak_ptr:

    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
    
    w = p        // P可以是share_ptr  或weak_ptr
    w.reset()    //w 为空
    w.use_count  //与w共享对象的shared_ptr竖向
    w.expired()  //w.use_count() 为0 ,返回true,否则返回为false
    w.lock()     //如果expired 为true,则返回一个空shared_ptr,否则返回一个指向w对象的shared_ptr

    weak_ptr  举例

    #include <iostream>
    #include <memory>
    #include <vector>
    
    using namespace std;
    
    class Test{
    public:
      Test(int d = 0) : data(d){cout << "new" << data << endl;}
      ~Test(){cout << "del" << data << endl;}
      void func(){cout << "func" << endl;}
    private:
      int data;
    };
    
    class teacher{
    public:
      teacher(){cout << "teacher()" << endl;}
      ~teacher(){cout << "del teacher" << endl;}
      shared_ptr<student> stu;
    };
    class student{
    public:
      student(){cout << "student()" << endl;}
      ~student(){cout << "del student" << endl;}
      //如果换成shared_ptr<teacher> tea;就会形成循环引用,导致内存泄漏    
      weak_ptr<teacher> tea;
    };
    int main(){                          
      //用weak_ptr解决了share_ptr循环引用,导致的内存不能释放的问题                          
      shared_ptr<teacher> tptr(new teacher);//计数器1                               
      shared_ptr<student> sptr(new student);//计数器1                               
      tptr->stu = sptr;//sptr的计数器2                                              
      sptr->tea = tptr;//不增加tptr的引用计数,因为tea是weak指针,如果tea是shared_ptr容易导致循环引用                    
      cout << tptr.use_count() << endl;//1                                          
      cout << sptr.use_count() << endl;//2                                          
      return 0;
    }

    std::move

    类型转换static_cast<typename remove_reference<T>::type &&>(t);
    std::move语句可以将左值变为右值而避免拷贝构造。将对象的状态或者所有权从一个对象转移到另一个对象。
    左值:放在等号左边的值,可以被赋值: a; ++a; *a;**a;a.m;a->m;a[m]
    右值:放在等号右边的值,不能被赋值;a+b; a++; a&&b; a<b; &a

    深拷贝:A先申请出一片新的空间,完全复制B的内容到新空间中;
    浅拷贝:A复制B指针,将自己的指针指向B的内容地址,A、B公用一块内存地址;
    A = std::move(B); A 对 B 实现浅层拷贝,为了防止B的改变影响到A ,对B清除。
    参考:https://www.lanindex.com/stdmove%E5%AE%9E%E9%99%85%E5%BA%94%E7%94%A8%E5%88%86%E6%9E%90/

    举例:

    #include <iostream>
    #include <utility>
    #include <vector>
    #include <string>
    int main()
    {
        std::string str = "Hello";
        std::vector<std::string> v;
        //调用常规的拷贝构造函数,新建字符数组,拷贝数据
        v.push_back(str);
        std::cout << "After copy, str is "" << str << ""
    ";//str is "hello"
        //调用移动构造函数,掏空str,掏空后,最好不要使用str
        v.push_back(std::move(str));
        std::cout << "After move, str is "" << str << ""
    ";//str is ""
        std::cout << "The contents of the vector are "" << v[0]
                                             << "", "" << v[1] << ""
    ";
    }
  • 相关阅读:
    MetaMask/metamask-extension-provider
    MetaMask/json-rpc-engine
    MetaMask/json-rpc-middleware-stream
    DamonOehlman/detect-browser
    kumavis/obj-multiplex
    java面试第三天
    java面试第二天
    JavaScript公共函数
    Eclipse快捷键大全
    Jdbc入门
  • 原文地址:https://www.cnblogs.com/heimazaifei/p/12133715.html
Copyright © 2011-2022 走看看