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

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

关注我

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

B站:

👨‍💻主页:天赐细莲 bilibili

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/572931.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

新手如何搭建测试平台?一文1800字从0到1实现【建议收藏】

01、职责 一个健康的测试平台体系&#xff0c;对测试人员的职责分工、协作模式会有不同的要求。 测试平台核心的职责是完成高质量的交付已满足业务需求。测试活动包括单元测试、集成测试、接口测试、性能测试等&#xff0c;都是通过这些测试手段&#xff0c;协同整个测试平台…

JavaEE 初阶篇-深入了解 UDP 通信与 TCP 通信(综合案例:实现 TCP 通信群聊)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 UDP 通信 1.1 DatagramSocket 类 1.2 DatagramPacket 类 1.3 实现 UDP 通信&#xff08;一发一收&#xff09; 1.3.1 客户端的开发 1.3.2 服务端的开发 1.4 实现 …

将Python机器学习模型集成到C++ Qt客户端应用程序中|Qt调用python详解

0、前言 有几个不同的选项可以将你的Python机器学习模型集成到你的C Qt客户端应用程序中。以下是一些可能的解决方案&#xff1a; 创建API&#xff1a; 将你的机器学习模型部署为一个API服务。你可以使用像Flask这样的轻量级Web框架来创建一个简单的HTTP服务。这样&#xff0…

Linux---Socket

网络套接字(socket) 网络通信仅仅是为了让两台主机间传送数据吗&#xff1f;数据是被谁需要的呢&#xff1f;--- 进程&#xff0c;所以网络通信的本质是两个进程间的通信。那么如何找到两台主机上的两个进程呢&#xff1f; 1、通过IP地址确定网络中的唯一一台主机 2、通过po…

Linux关闭swap分区操作[适用于CDH报警等]

1.查看swap分区挂载路径(没卵用) swapon -s 2.设置配置文件的swap配置 echo “vm.swappiness 0” > /etc/sysctl.conf 3.设置内存中的swap状态。有时候配置文件为0&#xff0c;但集群或服务仍然使用了swap分区&#xff0c;可能原因就是内存没有同步配置 echo “0” > …

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

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

jmeter之跨线程关联

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

Java知识总结---并发篇

线程 线程的状态 Java中线程可以有如下6中状态&#xff1a; NEW 新创建 RUNNABLE 可运行 BLOCKED 阻塞 WAITING 等待 TIMED WAITING 计时等待 TERMINATED 终止 线程的创建 &#xff08;1&#xff09;继承Thread类 public class ExtendsThread extends Thread { O…

后端工程师——C++工程师招聘需求

相比 Java 语言方向&#xff0c;C 入门简单&#xff0c;精通难&#xff0c;找工作竞争压力更小&#xff0c;但 C 依然是近年来招聘的热门岗位之一。本文将从以下三个方面进行详细讲解&#xff0c;帮助你对 C 相关岗位的就业前景、岗位要求、学习路线等有更充分的了解。 C工程师…

48-70V降12V/33V 5A高效同步降压DC-DC——AH1007

AH1007是一款高效率、高压外置MOSFET管降压芯片TEL&#xff1a;186*4884*3702*&#xff0c;芯片典型输入是8V~100V,输出 电压可调&#xff0c;AH1007最大输出电流可支持6A以上&#xff0c;需要注意板子的散热和温升。 AH1007典型开关频率为150KHz。轻载时会自动降低开关频率以…

SSRF—服务器请求伪造 漏洞详解

漏洞简述 SSRF(Server-Side Request Forgery:服务器端请求伪造) 是一种由攻击者构造&#xff0c;由服务端发起请求的一个网络攻击&#xff0c;一般用来在外网探测或攻击内网服务&#xff0c;其影响效果根据服务器用的函数不同&#xff0c;从而造成不同的影响。 SSRF 形成的原因…

H5点击复制功能 兼容安卓、IOS

效果图 HTML代码 <div>链接&#xff1a;<span style"color: #FF8A21" click"CopyUrl" id"copyId"> https://blog.csdn.net/qq_51463650?spm1000.2115.3001.5343</span> </div>复制方法 const CopyUrl () > {let …

水库大坝安全白蚁监测系统解决方案

一、系统背景 白蚁作为河岸生态系统中的重要病害&#xff0c;不仅会导致水库大坝外部环境发生改变&#xff0c;甚至会引发水库大坝破坏&#xff0c;进而导致自身结构失去稳定性&#xff0c;严重影响水库大坝的正常运行。因此&#xff0c;治理水库大坝白蚁是确保水库大坝工程顺利…

chrome 浏览器 f12 如何查看 websocket 消息?

1. 打开目标页面 2. f12--》网络--》WS&#xff0c;然后刷新页面( 如果不刷页面&#xff0c;就会看不到 websocket 请求&#xff0c;因为 websocket 是长连接&#xff0c;页面加载后只发出一次连接请求&#xff0c;不像 http 接口&#xff0c;不用刷新页面&#xff0c;待会儿也…

Linux Docker下载镜像更改默认存储位置/usr/lib/docker

用于解决docker默认存储位置磁盘空间不足&#xff0c;切换存储位置 1、执行下面命令查看 现在docker的存储位置 docker info | grep "Docker Root Dir" 1.2、如果之前已经下载过镜像可以用mv命令把原来的镜像复制到新的地址 mv /var/lib/docker /data/docker 2、…

十堰着力建设城市级供应链安全检测平台,全力打造数字政府安全示范区

4月23日&#xff0c;十堰市互联网信息办公室、十堰政务服务和大数据管理局、十堰数安技术有限公司共同签订了《城市供应链安全检测平台战略合作框架协议》&#xff0c;通过政企深度协作&#xff0c;加强十堰市数字安全体系建设&#xff0c;为推动十堰市乃至湖北省数字经济产业发…

RS1G08XF5规格详情

RS1G08XF5 是一款由润石&#xff08;RUNIC&#xff09;公司生产的电子元器件。根据所提供的信息&#xff0c;这是一款SOT-23-5封装的器件&#xff0c;其主要参数包括最小电源电压为5V&#xff0c;最大电源电压为6V&#xff0c;最小工作温度为-40C。 为了更准确地了解这款器件的…

【开源】I.Mx6uLL的QT项目合集

文章目录 前言仓库11.下载仓库代码2.使用qmake交叉编译3.效果展示 前言 不定期的更新基于I.Mx6uLL的arm架构下的QT项目合集&#xff1a; 项目移植前的准备 【环境安装】 仓库1 1.下载仓库代码 https://github.com/cocowts/QtCustomcomponent git clone https://github.com/…

ssm框架的网上招聘系统的设计与实现,ssm框架,java编程,mysql数据库 myeclipse开发平台

网上招聘是一个功能很复杂的系统&#xff0c;各个部门之间要有一定的协调能力。 要建立一个高效的管理系统的关键问题就是系统内部的各个模块的相互作用&#xff0c;简单的编写一个网站 只用html &#xff0c;css &#xff0c;javascript &#xff0c;xml &#xff0c;xsl技术…

XUbuntu18.04 源码编译Qt4.5.3的过程

由于新公司很多旧的软件都是基于这个版本做的嵌入式开发。 所以想要自己搭一套基于Linux的非嵌入式开发环境&#xff0c;方便用来调试和编译代码。 这样就可以完成在linux下开发&#xff0c;然后直接嵌入式打包&#xff0c;涉及到界面的部分就不需要上机调试看问题了。 所以…
最新文章