浅谈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计算。

原文地址:https://www.cnblogs.com/szdytom/p/cpp-auto-pointer.html