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

文章目录

  • 😁介绍
  • 🤔类外操作
    • 😅错误操作
    • 😂正确操作
  • 🤔类内操作
    • 😮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.mzph.cn/diannao/3188.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

carrier开利触摸屏ICVC控制面板维修CEPL130445

开利离心机19XR空调ICVC显示面板维修CEPL130445-03-R/04-R/02-R 人机界面触摸维修故障有&#xff1a;花屏、白屏、按触摸屏无反应或反应慢(触摸不好)、内容错乱、无背光、背光暗、有背光无字符、不能通信、按键无反应等&#xff08;可更换液晶屏&#xff09;黑屏、对触摸屏触摸…

QML 中的状态

Qt hello – 专注于Qt的技术分享平台 状态描述了当前用户界面样子&#xff0c;QML中一个状态定义了一组属性的改变&#xff0c;并且会在一定条件下被触发。 假设有这么一个场景&#xff0c;红黄绿三个灯&#xff0c;用一个按钮&#xff0c;点击后依次切换三个灯亮起。使用QWi…

js如何获取对象的属性值

获取对象的属性值&#xff0c;有两种方式。 方式一&#xff1a; 对象.属性名 let obj {name:张三,age:23 }; console.log(obj.name); //张三方式二&#xff1a; 对象[属性名] let obj {name:张三,age:23 }; console.log(obj[name]); //张三 两种方式有什么不同&am…

AWR报告采集

一、windows下采集 自动负载信息库&#xff08;Automatic Workload Repository&#xff0c;AWR&#xff09;是在Oracle 10g中被引入的&#xff0c;缺省地被安装到Oracle10g数据库中&#xff0c;用于收集关于该特定数据库的操作统计信息和其他统计信息。AWR的采样工作由后台进程…

Linux-线程互斥和死锁

目录 一.线程互斥 1.1 进程线程间的互斥相关背景概念 1.2 互斥量mutex 二.互斥量的接口 2.1 初始化互斥量 2.2 销毁互斥量 2.3 互斥量加锁和解锁 2.4 改进后售票代码 三.死锁 3.1.什么是死锁&#xff1f; 3.2.死锁四个必要条件 3.3 避免死锁 一.线程互斥 1.1 进程…

吴恩达2022机器学习专项课程(一) 6.1 动机第三周课后实验:Lab1使用逻辑回归进行分类

问题预览/关键词 什么是逻辑回归&#xff1f;什么是二分类问题&#xff1f;二分类问题案例如何表达二分类的结果&#xff1f;逻辑回归通常用哪种表达形式&#xff1f;什么是正样本和负样本&#xff1f;什么是阈值&#xff1f;可视化线性回归解决二分类线性回归面对二分类产生的…

Android 跨进程通信

Android中常用的跨进程通信方法有以下几种&#xff1a; Intent、Binder、AIDL、Messenger、ContentProvider。 Intent 可以通过Intent传递数据和消息&#xff0c;但是只能传递一些简单的数据类型&#xff0c;比如字符串、整数等。 示例&#xff1a; 从一个应用程序发送一个字…

jmeter之跨线程关联

1&#xff09;_setproperty函数&#xff1a;将值保存成jmeter属性 2&#xff09;_property函数&#xff1a;在其他线程组中使用property函数读取属性 一、跨线程接口引用变量 1. 法一&#xff1a;jmeter自带函数_setProperty和_property 1. 1线程组 01 创建登录的【HTTP请求】…

1、k8s问题pod从service中剔除

一、起因 redis原来由两服务器的集群变为三服务器的集群&#xff0c;通过statefulset扩展了两节点&#xff0c;并把redis-app-0和redis-app-3从集群中去除&#xff0c;但是由于service路由后端不变&#xff0c;导致程序连接后端仍然可能到redis-app-0和redis-app-3 二、处理 …

常用的跨平台笔记软件OneNote, GTD

keywords: OneNote, GTD 快捷键 C --> Ctrl S --> Shift M --> Alt Cmd --> Command 调整文本格式 功能WindowsMac OS X高亮选中文本C-S-h 或 C-M-h插入超链接C-kCmd-k复制选中文本的格式&#xff08;格式刷&#xff09;C-S-cCmd-M-c应用格式刷到选中文本C-…

分类算法——决策树(五)

认识决策树 决策树思想的来源非常朴素&#xff0c;程序设计中的条件分支结构就是if-else结构&#xff0c;最早的决策树就是利用这类结构分割数据的一种分类学习方法。 决策树分类原理详解 为了更好理解决策树具体怎么分类的&#xff0c;通过一个问题例子&#xff1a; 问题…

C++入门----内联函数auto范围fornullptr指针

1.内联函数 顾名思义&#xff0c;内联函数也是函数的一种&#xff0c;我们在C语言的学习过程里面知道了函数和宏之间的区别和各自的优缺点&#xff1b; 函数的使用需要建立栈帧&#xff0c;宏的使用需要考虑各种符号的优先级问题&#xff0c;很容易出错&#xff0c;因为宏在使…

HarmonyOS开发案例:【闹钟】

介绍 使用后台代理提醒&#xff0c;实现一个简易闹钟。要求完成以下功能&#xff1a; 展示指针表盘或数字时间。添加、修改和删除闹钟。展示闹钟列表&#xff0c;并可打开和关闭单个闹钟。闹钟到设定的时间后弹出提醒。将闹钟的定时数据保存到轻量级数据库。 相关概念 [Canva…

MySQL创建表时添加约束

创建表时添加约束 ①列级约束 演示&#xff1a; CREATE TABLE students( id INT PRIMARY KEY,#主建约束&#xff08;唯一性&#xff0c;非空&#xff09; s_name VARCHAR(10) NOT NULL, #非空 s_sex CHAR(1) CHECK(s_sex男 OR s_sex女),#检查约束(Mysql无效) s_seat INT UNI…

数学建模完整版

模型与适用题型 微分方程传染病预测模型 神经网络 层次分析法 粒子群算法 matlab 优劣解距离法

如何为Postgres数据库设置安全的访问控制和权限管理

文章目录 解决方案1. 使用角色和权限管理2. 配置认证方法3. 使用网络访问控制4. 定期审查和更新权限 示例代码1. 创建角色并分配权限2. 配置密码认证3. 配置网络访问控制 总结 PostgreSQL是一个功能强大的开源关系型数据库系统&#xff0c;提供了丰富的权限和访问控制机制&…

前端表单滑块验证码开发

Jsp页面用javascript加 滑动验证条_jsp验证码滑动实现-CSDN博客 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name&quo…

云原生技术助力某制造业巨头构建面向未来的统一技术中台

制造帝国的数字化绮梦 在全球经济一体化和信息化快速发展的今天&#xff0c;制造业的数字化转型已经成为推动产业高质量发展的关键趋势。中国政府高度重视制造业的数字化转型&#xff0c;将其视为实现制造强国战略的核心内容&#xff0c;并出台了一系列政策支持推动发展进程。…

LLM大语言模型(十二):关于ChatGLM3-6B不兼容Langchain 的Function Call

背景 基于本地的ChatGLM3-6B直接开发LangChain Function Call应用&#xff0c;发现其输出的action和action_input非常不稳定。 表现为生成的JSON格式回答非常容易出现不规范的情况&#xff0c;导致LangChain的Agent执行报错&#xff0c;或者进入死循环。 ChatGLM3-6B不兼容La…

基于北京迅为iTOP-RK3588大语言模型部署测试

人工智能&#xff08;AI&#xff09;领域中的大模型&#xff08;Large Model&#xff09;逐渐成为研究的热点。大模型&#xff0c;顾名思义&#xff0c;是指拥有海量参数和高度复杂结构的深度学习模型。它的出现&#xff0c;不仅推动了AI技术的突破&#xff0c;更为各行各业带来…