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

server/2024/11/15 4:46:45/

文章目录

  • 😁介绍
  • 🤔类外操作
    • 😅错误操作
    • 😂正确操作
  • 🤔类内操作
    • 😮std::enable_shared_from_this<>
      • 😮奇异递归模板 CRTP(Curiously Recurring Template Pattern)
      • 😮基本原理
      • 😮std::enable_shared_from_this<>
    • 😅错误操作
      • 😅shared_ptr 经典错误
      • 😅enable_shared_from_this 经典错误
    • 😂正确操作
  • 😁END

😁介绍

自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);// 执行拷贝构造,同时维护同一个pstd::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) {// safestd::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

  • std::shared_ptr - cppreference.com
  • std::enable_shared_from_this - cppreference.com

关注我

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

B站:

👨‍💻主页:天赐细莲 bilibili


http://www.ppmy.cn/server/15145.html

相关文章

考研日常记录(upd 24.4.24)

由于实在太无聊了 &#xff0c; 所以记录以下考研备考日常 &#xff0c; 增加一点成就感 &#xff0c; 获得一点前进动力。 文章目录 2024.4.18 周四课程情况&#xff1a;时间规划&#xff1a; 2024.4.19 周五课程情况&#xff1a;时间规划&#xff1a; 2024.4.20 周六2024.4.2…

mybatis快速入门-注解版

mybatis 使用注解&#xff0c;简化 xml 配置&#xff0c;汲及到动态 sql 或是多表查询&#xff0c;还是使用 xml 映射文件配置编写。(企业工作中&#xff0c;几乎全是 xml 配置&#xff0c;xml 的 sql 使用注解方式少,而类引用注解方式)。 注解 Select()&#xff1a;查询Inse…

尾矿库安全监测:仪器埋设与维护的关键要求

尾矿库作为矿业生产的重要组成部分&#xff0c;其安全运营对于保障人员生命安全和环境保护具有至关重要的意义。为了确保尾矿库的安全运行&#xff0c;及时发现潜在的安全隐患&#xff0c;必须采取有效的安全监测措施。本文将重点探讨尾矿库安全监测仪器的埋设及维护要求。 一、…

Centos7 的 Open Stack T 版搭建流程 --- (二)配置 SQL 数据库

配置 SQL 数据库 文章目录 配置 SQL 数据库&#xff08;1&#xff09;安装 MariaDB、MariaDB服务器 和 Python 2 PyMySQLcontroller &#xff08;2&#xff09;配置数据库文件controller &#xff08;1&#xff09;安装 MariaDB、MariaDB服务器 和 Python 2 PyMySQL controlle…

FANUC机器人socket通讯硬件配置

一、添加机器人选配包 Fanuc机器人要进行socket通讯&#xff0c;需要有机器人通讯的选配包&#xff0c;1A05B-2600-R648 User Socket Msg&#xff0c;1A05B-2600-R632 KAREL&#xff0c;1A05B-2600-R566 KAREL Diagnostic&#xff0c;1A05B-2600-J971 KAREL Use Sprt FCTN。 二…

在IDEA中使用.env文件配置信息

在软件开发过程中&#xff0c;经常需要使用配置信息来指定应用程序的行为&#xff0c;例如数据库连接参数、API密钥等。而将这些敏感信息硬编码在代码中是不安全的&#xff0c;因此通常会将其存储在配置文件中。 .env文件是一种常见的配置文件格式&#xff0c;它可以存储键值对…

新恒盛110kV变电站智能辅助系统综合监控平台+道巡检机器人

江苏晋控装备新恒盛化工有限公司是晋能控股装备制造集团有限公司绝对控股的化工企业&#xff0c;公司位于江苏省新沂市。新恒盛公司40•60搬迁项目在江苏省新沂市经济开发区化工产业集聚区苏化片区建设&#xff0c;总投资为56.64亿元&#xff0c;该项目是晋能控股装备制造集团重…

[C++][算法基础]线性同余方程(扩展欧几里得算法)

给定 &#x1d45b; 组数据 &#x1d44e;&#x1d456;,&#x1d44f;&#x1d456;,&#x1d45a;&#x1d456;&#xff0c;对于每组数求出一个 &#x1d465;&#x1d456;&#xff0c;使其满足 &#x1d44e;&#x1d456;&#x1d465;&#x1d456; ≡ &#x1d44f;&am…