zoukankan      html  css  js  c++  java
  • C++11智能指针

      为了解决C++内存泄漏的问题,C++11引入了智能指针(Smart Pointer)。

      智能指针的原理是,接受一个申请好的内存地址,构造一个保存在栈上的智能指针对象,C++中有一个重要原则,在函数结束时(不论是正常返回,还是因为异常除法的对战回退),会将所有栈对象销毁,也就是会调用所有栈对象的析构函数。智能指针内部保存的内存也就被释放掉了(除非将智能指针保存起来)。

      C++11提供了三种智能指针:std::shared_ptr, std::unique_ptr, std::weak_ptr,使用时需添加头文件<memory>。

      shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。每使用他一次,内部的引用计数加1,每析构一次,内部的引用计数减1,减为0时,删除所指向的堆内存。shared_ptr内部的引用计数是安全的,但是对象的读取需要加锁。

    1. shared_ptr

    1.1 基本用法

    (1)初始化

      可以通过构造函数、std::make_shared<T>辅助函数和reset方法来初始化shared_ptr

    #include "stdio.h"
    #include<memory>
    #include<iostream>
    using namespace std;
    
    void main()
    {
        shared_ptr<int> p1(new int(10));
        cout << "p1的引用计数:"<<p1.use_count() << endl;
        shared_ptr<int> p2 = p1; 
        cout << "p1的引用计数:" << p1.use_count() << " p2的引用计数:" << p2.use_count() << endl;
        p2.reset(new int(2));   // 当智能指针中有值时,调用rest会使引用计数减1
        cout << "p1的引用计数:" << p1.use_count() << " p2的引用计数:" << p2.use_count() << endl;
        cout << *p2 << endl;
        p2.reset(); 
        cout << "p1的引用计数:" << p1.use_count() << " p2的引用计数:" << p2.use_count() << endl;
    
        shared_ptr<int> p3 = make_shared<int>(20); 
        shared_ptr<int> p4;
        p4.reset(new int(30));
        cout << "p4的引用计数:" << p4.use_count() << endl;
    }

    注意:不能将一个原始指针直接赋值给一个智能指针

    std::shared_ptr<int> p4 = new int(1);// error

      reset()包含两个操作。当智能指针中有值的时候,调用reset()会使引用计数减1.当调用reset(new xxx())重新赋值时,智能指针首先是生成新对象,然后将就对象的引用计数减1(当然,如果发现引用计数为0时,则析构旧对象),然后将新对象的指针交给智能指针保管。

    (2)获取原始指针

    std::shared_ptr<int> p4(new int(5));
    int *pInt = p4.get();
    

    (3)指定删除器

      智能指针可以指定删除器,当智能指针的引用计数为0时,自动调用指定的删除器来释放内存。std::shared_ptr可以指定删除器的一个原因是其默认删除器不支持数组对象,这一点需要注意。

    template<class T, class D>
    shared_ptr(T *p, D d)
    void DeleteIntPtr(int *p)
    {
         delete p;      
    }
    
    shared_ptr<int> p(new int(1), DeleteIntPtr);

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

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

      也可以使用syd::default_delete作为删除器,default_delete的内部是通过调用delete来实现的

    shared_ptr<int> p(new int[10], deault_delete<int[]>);

    (4)注意make_share和make_arrar_share的使用

    (5)使用shared_ptr注意事项

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

    b. 不要在函数实参中创建shared_ptr

    2. unique_ptr独占指针

      unique_ptr是一个独占型的智能指针,它不允许其它的智能指针共享其内部的指针,不允许通过赋值将一个unique_ptr赋值给另外一个unique_ptr,unique_ptr不允许复制,但可以通过函数返回给其它的unique_ptr,还可以通过move来转移到其它的unique_ptr,这样它本身就再拥有原来指针的所有权。

    注意:unique_ptr不支持make_share

    3. weak_ptr弱引用指针

      弱引用指针week_ptr是用来监视shared_ptr的,不会使引用计数加1,它不管理shared_ptr内部的指针,week_ptr没有重载操作符,不共享指针,不操作资源。weak_ptr指针可以用来返回this指针和解决循环引用的问题

    (1)use_count获取当前观测资源的引用计数

    shared_ptr<int> p(new int(1));
    weak_ptr<int> p1(p);
    cout << p1.use_count() <<endl;

    (2)通过expired()来判断观测的资源是否已经被释放

    (3)通过lock()来获取监视的shared_ptr

    (4)weak_ptr返回this指针

      不能直接将this指针返回为shared_ptr,需要通过派生std::enable_shared_from_this类,并通过其方法shared_from_this来返回智能指针,原因是enable_shared_from_this类中有一个weak_ptr指针,这个weak_ptr用来观测this智能指针,调用shared_from_this,会调用内部的weak_ptr的lock()方法

    (5 )weak_ptr解决循环引用的问题

    #include "stdio.h"
    #include<memory>
    #include<iostream>
    using namespace std;
    
    class B;
    class A
    {
    public:
        shared_ptr<B> bptr;
        ~A()
        {
            cout << "A is delete!" << endl;
        }
    };
    
    class B
    {
    public:
        shared_ptr<A> aptr;
        ~B()
        {
            cout << "B is delete!" << endl;
        }
    };
    void main()
    {
        {
            shared_ptr<A> a(new A());
            shared_ptr<B> b(new B());
    
            a->bptr = b;
            b->aptr = a;
    
            cout << "A的引用计数:" << a.use_count() << endl;
            cout << "B的引用计数:" << b.use_count() << endl;
        }
    
        int i = 0;
    }

    离开作用域后引用计数减1,不会去删除指针,导致内存泄漏

    通过weak_ptr解决该问题,只要将A或B中的任意一个成员改成weak_ptr即可

  • 相关阅读:
    书籍推荐:《C#7.0本质论》
    KPI在小型产品团队中的实践
    程序员如何学习英语
    端到端测试实践:Jenkins集成TestCafe
    对产品质量的一点思考
    不断进化的分支和需求管理
    书籍推荐:《More Effective C#》
    带你了解C#每个版本新特性
    不断进化的分支和需求管理
    怎样学习和阅读技术书籍?
  • 原文地址:https://www.cnblogs.com/xiaobingqianrui/p/9186153.html
Copyright © 2011-2022 走看看