(C++) 内类生成智能指针shared_from_this介绍

😁介绍

自C++11起,有三大智能指针:

  • unique_ptr
  • shared_ptr
  • weak_ptr

都是内存管理中的非常重要的一部分动态内存管理 - cppreference.com

其中shared_ptr在实际应用中具有非常广泛的应用。而拷贝操作也是非常常见和重要的操作。在类外可以直接使用默认的拷贝构造和拷贝赋值,而类内呢?显然这两种默认的拷贝操作均不适用。

本文就是讲解使用std::enable_shared_from_this::shared_from_this()来处理该问题。

🤔类外操作

😅错误操作

使用原始指针构造。

这是一种非常经典的错误。虽然构造出的智能指针都被多个smart_ptr掌管了,但是并非同一个控制块来操作,是随着每次构造都有一个控制块。

因此下方示例代码会产生两次析构。严重情况下可以让项目程序直接崩溃。

#include <iostream>
#include <memory>

struct Node {
    ~Node() {
        std::printf("[%p] %s\n", this, __func__);
    }
};

void test(Node* p) {
    // errro 同一个对象析构了两次
    std::shared_ptr<Node> sp1(p);
    std::shared_ptr<Node> sp2(p);
}

int main() {
    Node* p = new Node{};
    test(p);

    std::printf("[%u] %s end\n", __LINE__, __func__);
}

😂正确操作

直接使用拷贝操作。

对于shared_ptr默认的拷贝操作,可以由多个shart_ptr都指向同一个对象,并由同一个控制块维护。

#include <iostream>
#include <memory>

struct Node {
    ~Node() {
        std::printf("[%p] %s\n", this, __func__);
    }
};

void test(Node* p) {
    std::shared_ptr<Node> sp1(p);
    // 执行拷贝构造,同时维护同一个p
    std::shared_ptr<Node> sp2 = sp1;
}

int main() {
    Node* p = new Node{};
    test(p);

    std::printf("[%u] %s end\n", __LINE__, __func__);
}

🤔类内操作

😮std::enable_shared_from_this<>

😮奇异递归模板 CRTP(Curiously Recurring Template Pattern)

CRTP是一种模板元编程技术,它允许一个类模板通过基类的形式捕获自身的类型。

这种模式的名字来源于其自身的递归使用,“Curiously” 指的是这种模式的非直观性,因为在通常情况下,基类通常是在类定义之后才被引用的,而在这个模式中,基类是在类定义的开始就引用了尚未完全定义的类。

😮基本原理

CRTP的基本原理是利用模板类和继承。在CRTP中,一个类通过继承一个模板类,并将自身作为模板参数传递给基类模板。这样做的好处是可以在派生类中使用基类模板中的成员,而这些成员依赖于派生类的类型。

😮std::enable_shared_from_this<>

std::enable_shared_from_this 能让其一个对象(假设其名为 t,且已被一个 std::shared_ptr 对象 pt 管理)安全地生成其他额外的 std::shared_ptr 实例(假设名为 pt1, pt2, ...),它们都与 pt 共享对象 t 的所有权。

其中有两个核心函数:

  • shared_from_this
  • weak_from_this()
#include <memory>
// 奇异递归模板 CRTP(Curiously Recurring Template Pattern)
struct Node : public std::enable_shared_from_this<Node> { };

😅错误操作

😅shared_ptr 经典错误

还是先展示一个经典错误。

由于在类内的成员函数,(没有额外操作的情况下)只能访问到this指针。

#include <iostream>
#include <memory>

struct Node {
    ~Node() {
        std::printf("[%p] %s\n", this, __func__);
    }

    std::shared_ptr<Node> make_this_share_ptr() {
        // 经典错误
        return std::shared_ptr<Node>(this);
    }
};

void test(Node* p) {
    std::shared_ptr<Node> sp1 = p->make_this_share_ptr();
    std::shared_ptr<Node> sp2 = p->make_this_share_ptr();
}

int main() {
    Node* p = new Node{};
    test(p);

    std::printf("[%u] %s end\n", __LINE__, __func__);
}

😅enable_shared_from_this 经典错误

回到本文的重头戏,这里使用奇异递归模板的方式继承std::enable_shared_from_this<>,并使用shared_from_this()创建出指向自己的share_ptr。

但如果直接使用,还是会出现一个经典错误。

#include <iostream>
#include <memory>

struct Node : public std::enable_shared_from_this<Node> {
    ~Node() {
        std::printf("[%p] %s\n", this, __func__);
    }

    std::shared_ptr<Node> make_this_share_ptr() {
        return shared_from_this();
    }
};

void test(Node* p) {
    // 事先没有创建任何`std::shared_ptr`实例
    std::shared_ptr<Node> sp = p->make_this_share_ptr();
}

int main() {
    Node* p = new Node{};
    test(p);

    std::printf("[%u] %s end\n", __LINE__, __func__);
}

即:在没有使用构造出std::shared_ptr,就直接调用了shared_from_this(),会抛出异常。

ref: 若在先前未由 std::shared_ptr 共享的对象上调用 shared_from_this,则抛出 std::bad_weak_ptr

在mingw-gcc中输出如下:

terminate called after throwing an instance of 'std::bad_weak_ptr'
what():  bad_weak_ptr

😂正确操作

正确操作即为先构造出一个std::shared_ptr再使用shared_from_this()

保证了前提安全后,不论是std::shared_ptr还是裸指针去操作shared_from_this()都不会出现问题。

#include <iostream>
#include <memory>

struct Node : public std::enable_shared_from_this<Node> {
    ~Node() {
        std::printf("[%p] %s\n", this, __func__);
    }

    std::shared_ptr<Node> make_this_share_ptr() {
        return shared_from_this();
    }
};

void test(Node* p) {
    // safe
    std::shared_ptr<Node> sp0(p);
    std::shared_ptr<Node> sp1 = sp0;
    std::shared_ptr<Node> sp2 = sp1->make_this_share_ptr();
    std::shared_ptr<Node> sp3 = p->make_this_share_ptr();
}

int main() {
    Node* p = new Node{};
    test(p);

    std::printf("[%u] %s end\n", __LINE__, __func__);
}

😁END

ref

关注我

关注我,学习更多C/C++,算法,计算机知识

B站:

👨‍💻主页:天赐细莲 bilibili

  • 23
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`std::enable_shared_from_this` 是一个模板类,其目的是为了解决在一个对象中保存 shared_ptr 的问题。它是 C++11 引入的一个特性。 在使用 `std::shared_ptr` 时,我们往往需要在对象中保存一个 `std::shared_ptr` 的副本,这样才能确保对象在使用完毕后不会被提前销毁。但是这种方式会导致一些问题,比如我们无法防止用户直接使用裸指针来操作对象,从而导致对象被提前销毁等问题。 这时候,我们可以使用 `std::enable_shared_from_this` 来解决这些问题。具体而言,我们需要继承 `std::enable_shared_from_this`,然后在对象中使用 `shared_from_this()` 方法来获取一个指向当前对象的 `std::shared_ptr`。 下面是一个示例代码: ```c++ #include <iostream> #include <memory> class MyClass : public std::enable_shared_from_this<MyClass> { public: std::shared_ptr<MyClass> get_shared_ptr() { return shared_from_this(); } }; int main() { std::shared_ptr<MyClass> p(new MyClass); std::shared_ptr<MyClass> q = p->get_shared_ptr(); std::cout << "p.use_count() = " << p.use_count() << std::endl; std::cout << "q.use_count() = " << q.use_count() << std::endl; return 0; } ``` 在这个示例中,我们定义了一个名为 `MyClass` 的类,并且继承了 `std::enable_shared_from_this`。然后,我们在类中定义了一个名为 `get_shared_ptr()` 的方法,该方法使用了 `shared_from_this()` 方法来获取一个指向当前对象的 `std::shared_ptr`。在 `main()` 函数中,我们先创建了一个 `std::shared_ptr` 对象 `p`,然后通过 `p` 调用 `get_shared_ptr()` 方法获取了一个指向同一个对象的 `std::shared_ptr` 对象 `q`。最后,我们输出了 `p` 和 `q` 的引用计数,可以看到它们的引用计数都是 2。 需要注意的是,在使用 `std::enable_shared_from_this` 时,我们需要确保对象已经被一个 `std::shared_ptr` 管理,否则使用 `shared_from_this()` 方法会导致程序崩溃。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值