【智能指针】std::unique_ptr 和weak_ptr 基本用法

std::unique_ptr 

std::unique_ptr 是一种独占的语义,即只允许一个智能指针引用裸指针,这区别于 std::shared_ptr 允许多个 shared_ptr 引用同一个裸指针,它没有引用计数,它的性能比 shared_ptr 会高一点。

在用法上 std::unique_ptr 和 std::shared_ptr 是类似的,主要的不同是 std::unique_ptr 之间的赋值需要通过 std::move 实现。

在 code2 目录下新建一个 code5.cpp 文件:

#include 
#include 
#include 

struct A{
    ~A(){
        std::cout<<"destruct A"< p(new int(1));
    std::cout<<*p< p(new A);
        std::unique_ptr p1 = std::move(p);
    }

    {
        std::unique_ptr p(new A[3]);
    }

    {
        std::unique_ptr> p(new A, [](A* ptr){
            std::cout << "delete from a custom deleter...\n";
            delete ptr;
        });
    }
}

编译和运行代码:在 build 目录下执行

g++ ../code5.cpp -o code5 -std=c++11 && ./code5

输出结果:

1
destruct A
destruct A
destruct A
destruct A
delete from a custom deleter...
destruct A

构造 unique_ptr 的时候第二个参数是一个自定义删除器,如果不填写自定义删除器,就会使用默认的删除器,一般情况下我们用默认的删除器就可以了,如果有需要也可以写自定义的删除器。注意 C++11 中构造 std::unique_ptr 不能像 std::shared_ptr 那样(通过 make_shared 创建)通过 make_unique 方式去创建,make_unique 在 C++14 中才提供,当然也可以自己实现一个,具体实现这里不再赘述。

std::weak_ptr

std::weak_ptr 是用来监视 std::shared_ptr 的,通过 weak_ptr 就可以得知它监视的 shared_ptr 是否已经销毁了。

在 code2 目录下新建一个 code6.cpp 文件:

#include 
#include 

std::weak_ptr gw;

void f()
{
    if (auto spt = gw.lock()) { // 使用之前必须复制到 shared_ptr
        std::cout << *spt << "\n";
    }
    else {
        std::cout << "gw is expired\n";
    }
}

int main()
{
    {
        auto sp = std::make_shared(42);
        gw = sp;

        f();
    }

    f();
}

编译和运行代码:在 build 目录下执行

g++ ../code6.cpp -o code6 -std=c++11 && ./code6

输出结果:

42
gw is expired

std::weak_ptr 需要通过 std::shared_ptr 构造,当所监视的 shared_ptr 析构之后,通过 weak_ptr 的 lock 方法返回的是一个 nullptr ,如果 shared_ptr 仍然存在则返回 shared_ptr 。

https://www.shiyanlou.com/courses/1414/learning/?id=14993

通过 shared_ptr 保证安全地回调

shared_ptr 的一个重要作用就是在回调的时候是安全的,因为回调的时候,该回调函数的宿主对象可能已经被销毁了,那么在后面某个时刻回调的时候,实际上该对象已经不存在了,这时候再执行回调函数就是错误的。通过 shared_ptr 可以解决这个问题,保证安全地回调。

本小结内容可以先无脑动手做一下,到了实验四的时候就可以理解这样做的好处了。

在 code2 目录下新建一个 code7.cpp 文件:

#include 
#include 
#include 

class person : public std::enable_shared_from_this {

public:
    void update(){
        name_ = "jack";
        callback_();
    }

    void init_callback(){
        callback_ = [this]{
            test();
        };
    }

    void test(){
        auto self = this->shared_from_this();
        std::cout< callback_;
    std::string name_ = "tom";
};

int main(){
    person* ptr = nullptr;

    {
        std::shared_ptr p = std::make_shared();
        p->init_callback();

        ptr = p.get();
    }

    ptr->update();
}

编译和运行代码:在 build 目录下执行

g++ ../code7.cpp -o code7 -std=c++11 && ./code7

输出结果:

terminate called after throwing an instance of 'std::bad_weak_ptr'
  what():  bad_weak_ptr
[1]    209 abort (core dumped)  ./code7

会出现一个运行错误,因为在调用 ptr->update() 时,该 ptr 已经被前面的 shared_ptr 释放了,这时候这个 ptr 实际上是无效的。

为了解决这个问题,我们可以通过派生于 std::enable_shared_from_this 来通过对象自身得到 shared_ptr ,然后在设置回调的 lambda 中捕获该 shared_ptr ,这样,lambda 表达式中的 shared_ptr 和 lambda 回调函数的生命周期一致了。

在 code2 目录下新建一个 code8.cpp 文件:

#include 
#include 
#include 

class person : public std::enable_shared_from_this {

public:
    void update(){
        name_ = "jack";
        callback_();
    }

    void init_callback(){
        auto self = this->shared_from_this();
        callback_ = [this, self]{
            test();
        };
    }

    void test(){
        auto self = this->shared_from_this();
        std::cout< callback_;
    std::string name_ = "tom";
};

int main(){
    person* ptr = nullptr;

    {
        std::shared_ptr p = std::make_shared();
        p->init_callback();

        ptr = p.get();
    }

    ptr->update();
}

编译和运行代码:在 build 目录下执行

g++ ../code8.cpp -o code8 -std=c++11 && ./code8

输出结果:

jack

关键的一句是 lambda 捕获了 shared_ptr ,即使外面的 shared_ptr 重置了,但是 lamdba 表达式中捕获的 shared_ptr 仍然存在,引用计数不会减为 0,可以保证回调回来的时候,该对象仍然存在。

尽量使用std::make_unique和std::make_shared而不直接使用new

让我们从对齐std::make_unique 和 std::make_shared这两块开始。std::make_shared是c++11的一部分,但很可惜std::make_unique不是。它是在c++14里加入标准库的。假如你在使用c++11,也别担心,你很容易写出一个基本的版本。看这里:

template
std::unique_ptr make_unique(Ts&&... params)
{
    return std::unique_ptr(new T(std::forward(params)...));
}

正如你看到的,make_unique完美传递了参数给对象的构造函数,从一个原始指针构造出一个std::unique_ptr,返回创建的std::unique_ptr。这个形式的函数不支持数组和定制删除器(见条款18),但它证明了一点点的努力就可以根据需要创建一个make_unique。要记住的是不要把你的版本放到std命名空间里,因为你不想当升级到c++14后会和库提供的标准实现冲突吧。

尽量使用的原因:https://blog.csdn.net/bandaoyu/article/details/112197053

你可能感兴趣的:(java,c++,python,cmake,qt)