zoukankan      html  css  js  c++  java
  • 浅谈C++的智能指针

    C++11STL中新增了智能指针,可以有效的防止内存泄露的问题,以下代码除特殊说明均在C++11下编译。

    头文件

    #include <memory>
    

    shared_ptr 共享指针

    共享指针可以方便的在不同函数中传递,并且在所有指针实例被销毁时自动释放空间。
    shared_ptr通过引用计数来管理引用状态,每次有新的引用时计数加1,有引用销毁是计数减1,直到计数为0时自动释放内存空间。

    只要将new运算符返回的指针p交给一个shared_ptr对象“托管”,就不必担心在哪里写delete p语句——实际上根本不需要编写这条语句,托管pshared_ptr对象在消亡时会自动执行delete p
    妈妈再也不用担心我忘记delete啦

    通过shared_ptr的构造函数,传入让shared_ptr托管的指针。
    例如:

    shared_ptr<int> p(new int);
    

    在函数见传递shared_ptr,示例代码:

    #include <memory>
    #include <cstdio>
    using namespace std;
    
    struct node {
        int a, b, c;
        char e;
    };
    
    int foo(shared_ptr<node> a) {
        // p2被传入foo,a指向p2,引用计数加1,现在为3
        return a->a + a->b;
        // foo函数结束,a被释放,引用计数减1,现在为2
    }
    
    // 听说从main函数开始阅读程序是一个好习惯
    int main() {
        shared_ptr<int> p(new node); // p引用node对象的实例,引用计数现在为1
        shared_ptr<int> p2 = p;      // p2引用p,引用计数加1,现在为2
    
        foo(p2);
        return 0;
        // main函数结束,p,p2被释放,引用计数减2,现在为0
        // 引用计数为0,自动释放node的实例。
    }
    

    shared_ptr可以通过get成员函数获取普通指针:

    shared_ptr<int> p(new node);
    printf("%p
    ", p.get()); // 输出p指向的实例的地址
    

    shared_ptr重载了->*运算符,可以直接访问目标对象:

    shared_ptr<int> p(new node);
    p->a = 5;
    printf("%d
    ", p->a);
    printf("%d
    ", (*p).a);
    

    在C++17中从重载[]运算符,用于访问数组:

    shared_ptr<int[]> p(new int[105]);
    p[1] = 1;
    p[2] = 5;
    for (int i = 3; i <= 100; ++i)
        p[i] = i;
    
    int sum = 0;
    for (int i = 3; i <= 100; ++i)
        sum += p[i];
    

    什么,你想在C++11中用[]运算符?那么,有请下一位出场。

    unique_ptr 单引用指针

    unique_ptr一样会自动释放内存,但是只能用一个unique_ptr指向目标,其他的都会失效。
    Rust中的所有权机制一样。

    在函数见传递unique_ptr,示例代码:

    #include <memory>
    #include <cstdio>
    using namespace std;
    
    struct node {
        int a, b, c;
        char e;
    };
    
    unique_ptr<node> foo(unique_ptr<node> a) {
        // p2被传入foo,a获取p2的所有权,p2失效。
        a->a += a->b;
        return a;
        // foo函数结束,但是a被作为返回值返回,a拥有所有权。
    }
    
    void bar(unique_ptr<node> &a) {
        // p2被传入foo,a是p2的引用,p2拥有所有权。
        a->a += a->c;
        return;
        // foo函数结束,a被销毁,p2拥有所有权。
    }
    
    // 听说从main函数开始阅读程序是一个好习惯
    int main() {
        unique_ptr<int> p(new node); // p引用node对象的实例。
        unique_ptr<int> p2 = p;      // p2获取p的所有权,现在p失效。
        printf("%p
    ", p.get()); // p已经失效,输出0x00000000。
        printf("%p
    ", p1.get()); // 输出p2指向的地址。
    
        // 下面是两种函数传递unique_ptr的方式
        p2 = foo(p2); // foo函数返回a,p2重新取回所有权。
        bar(p2);
        return 0;
        // main函数结束,p2被销毁,内存自动释放。
    }
    

    unique_ptr可以通过get成员函数获取普通指针:

    unique_ptr<int> p(new node);
    printf("%p
    ", p.get()); // 输出p指向的实例的地址
    

    unique_ptr重载了->*运算符,可以直接访问目标对象:

    unique_ptr<int> p(new node);
    p->a = 5;
    printf("%d
    ", p->a);
    printf("%d
    ", (*p).a);
    

    unique_ptr从重载[]运算符,用于访问数组:

    shared_ptr<int[]> p(new int[105]);
    p[1] = 1;
    p[2] = 5;
    for (int i = 3; i <= 100; ++i)
        p[i] = i;
    
    int sum = 0;
    for (int i = 3; i <= 100; ++i)
        sum += p[i];
    

    weak_ptr 反向指针

    不知道大家在之前看shared_ptr时是否发现,如果有两个对象相互引用,他们的引用计数永远不会归0,他们永远不会被释放,产生循环引用

    -------------------                  -------------------
    | class A         |                  | class B         |
    |-----------------|                  |-----------------|
    |int x;           |  use each other  |int y;           |
    |shared_ptr<B> b<--------------------->shared_ptr<A> a |
    |-----------------|                  |-----------------|
    

    这时就需要weak_ptr出场了:

    -------------------                  -------------------
    | class A         |                  | class B         |
    |-----------------|                  |-----------------|
    |int x;           |one direction use |int y;           |
    |shared_ptr<B> b---------------------->weak_ptr<A> a   |
    |-----------------|                  |-----------------|
    

    这是因为weak_ptr的引用不会被shared_ptr计算。

  • 相关阅读:
    java注解-笔记
    java重载与重写-笔记
    java中(equals与==)- 笔记
    Java迭代与递归-笔记
    C++指针悬挂-笔记
    极速倒入sql记录到excel表格,19个子段5万条记录只需30秒
    利用MCI的方法可以方便的实现光驱门的开关
    如何让你的程序在任务列表隐藏
    如何实现遍历文件夹中的所有文件
    识别操作系统版本
  • 原文地址:https://www.cnblogs.com/szdytom/p/cpp-auto-pointer.html
Copyright © 2011-2022 走看看