2023-11-03 C++ 类型擦除与状态保留


点击 <C 语言编程核心突破> 快速C语言入门


C++ 类型擦除与状态保留

  • 前言
  • 一、一个正常的继承和多态
  • 二、一个不太正常的继承和多态
  • 三、试图构建能类型抹除但保留状态的mySharedPtr类
  • 总结


前言

要解决问题:
在C++中, 类型决定着对象的数据存储和解释, 以及方法.

通过继承和虚函数, 可以实现多态.

本文写一条邪路, 试图不用virtual修饰析构函数, 但在delete基类指针时, 调用子类的析构函数.

想到的思路:
我能想到的唯一思路, 是通过模板实现泛型, 通过lambda或函数指针抹除类型信息, 保留类型状态.

其它的补充:
本文完全是邪路子, 不要学, 只是作为C++灵活性的某种探讨, 虚函数才是正路.


一、一个正常的继承和多态

最简化模型, 只有析构函数, virtual修饰, 正常情况直接delete可以调用正确的析构函数, 使用shared_ptr也正确:

#include <cstdio>
#include <memory>using std::shared_ptr;class Base
{public:virtual ~Base(){printf("~Base()\n");}
};class Derive : public Base
{public:~Derive() override{printf("~Derive()\n");}
};auto main() -> int
{Base *ptr = new Derive;delete ptr;shared_ptr<Base> test(new Derive);return 0;
}

二、一个不太正常的继承和多态

我们人为的不给基类析构函数加virtual修饰, 看看结果:

#include <cstdio>
#include <memory>using std::shared_ptr;class Base
{public:~Base(){printf("~Base()\n");}
};class Derive : public Base
{public:~Derive(){printf("~Derive()\n");}
};auto main() -> int
{Base *ptr = new Derive;delete ptr;shared_ptr<Base> test(new Derive);return 0;
}

结果是调用子类析构错误, 如果含有资源, 那么就已经泄漏了.

~Base()
~Derive()
~Base()  

然而, 你没看错, 使用shared_ptr智能指针, 没有错误析构, 非常奇怪,

细究起来, 除非能够在存储时抹除类型(化为基类指针), 调用时使用正确类型(化为子类指针)才能达到这种效果, C++有这种方法么, 鄙人愚拙, 实在是只掌握C++皮毛, 除了虚函数, 好像没见过其它实现手段.

三、试图构建能类型抹除但保留状态的mySharedPtr类

类型一旦抹除, 将无法保留, 但可以通过模板函数或lambda, 保留状态.

下面的代码就是用模板函数保留类型的状态,

其实就是利用模板的性质, 在编译期间, 产生一个接收void*万能指针, 但在内部能处理特定类型指针的函数, 详见funcDelete.

#include <cstdio>
#include <memory>using std::shared_ptr;class Base
{public:~Base(){printf("~Base()\n");}
};class Derive : public Base
{public:~Derive(){printf("~Derive()\n");}
};// 形参抹除指针类型, 内部保留类型状态
template <typename Temp>
void funcDelete(void *data)
{// 由于data类型信息被抹除, 无法使用dynamic_castdelete static_cast<Temp *>(data);
}// 简化funcDelete函数指针类型
using delType = void (*)(void *);template <typename Temp>
struct mySharedPtr
{// 构造函数, 抹除data类型, 通过funcDelete<>函数指针保存类型状态template <typename TempDerive>explicit mySharedPtr(TempDerive *data_): data(data_){deleter = funcDelete<TempDerive>;}// 析构函数, 调用有原始指针类型的deleter函数指针~mySharedPtr(){deleter(data);}private:Temp *data = nullptr;delType deleter = nullptr;
};auto main() -> int
{Base *ptr = new Derive;delete ptr;shared_ptr<Base> test(new Derive);mySharedPtr<Base> testB(new Derive);return 0;
}

还有更为简洁的, 或说更为C++的方法, 通过lambda抹除类型, 保留状态:

#include <cstdio>
#include <functional>
#include <memory>using std::shared_ptr;class Base
{public:~Base(){printf("~Base()\n");}
};class Derive : public Base
{public:~Derive(){printf("~Derive()\n");}
};template <typename Temp>
struct mySharedPtr
{// 构造函数, 抹除data类型, 通过lambda保存类型状态, 也就是原始指针template <typename TempDerive>explicit mySharedPtr(TempDerive *data_): data(data_){deleter = [data_]() { delete data_; };}// 析构函数, 调用有原始指针类型的deleter函数指针~mySharedPtr(){deleter();}private:Temp *data = nullptr;std::function<void()> deleter = nullptr;
};auto main() -> int
{Base *ptr = new Derive;delete ptr;shared_ptr<Base> test(new Derive);mySharedPtr<Base> testB(new Derive);return 0;
}

通过lambda自行推断捕捉的变量类型, 并保留在内部状态中,

此操作很变态, 极度简洁且高效, 但我个人觉得有个小问题,

就是每个lambda都是一个类型, 这个和函数指针有天壤之别, 所以运行效率不一定比原始的函数指针好.


总结

通过上面示例, 我们实现了不用虚函数达到基类指针调用子类析构的目的, 比虚函数的实现还是要复杂一些, 并且不是太推荐, 只是作为一种尝试, 毕竟C++灵活性确实是可以支持你为所欲为.


点击 <C 语言编程核心突破> 快速C语言入门


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

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

相关文章

R语言使用surveyCV包对NHANES数据(复杂调查加权数据)进行10折交叉验证

美国国家健康与营养调查&#xff08; NHANES, National Health and Nutrition Examination Survey&#xff09;是一项基于人群的横断面调查&#xff0c;旨在收集有关美国家庭人口健康和营养的信息。 地址为&#xff1a;https://wwwn.cdc.gov/nchs/nhanes/Default.aspx 既往咱们…

ES6、ES7、ES8的特性是什么?

ES6、ES7、ES8都是JavaScript语言的版本,它们具有一些新的特性和变化。 ES6(ECMAScript 2015)引入了很多重要的新特性,包括: 1: 类(class):对熟悉Java,object-c,c#等纯面向对象语言的开发者来说,都会对class有一种特殊的情怀。ES6 引入了class(类),让JavaScri…

Kubernetes群集调度

调度约束 Kubernetes 是通过 List-Watch 的机制进行每个组件的协作&#xff0c;保持数据同步的&#xff0c;每个组件之间的设计实现了解耦。 用户是通过 kubectl 根据配置文件&#xff0c;向 APIServer 发送命令&#xff0c;在 Node 节点上面建立 Pod 和 Container。 APIServ…

Linux 服务器 Oracle19C安装

原文:【精选】Oracle | CentOS7安装Oracle19c数据库(RPM包)_oracle-database-preinstall-19c-1.0-1.el7.x86_64.rp_Thorolds Deer的博客-CSDN博客 下载 第一个软件包:Oracle Database 19c Download for Linux x86-64 第二个包:Oracle Linux 7 (x86_64) Latest | Oracle,…

Python-loguru-跨进程的日志服务器-django

文章目录 1.安装2.基础配置3.具体使用4.总结 1.安装 pip install loguru2.基础配置 可以在包的初始化文件中使用。 # -*- coding : utf-8-*- from pathlib import Path from loguru import logger#初始化日志系统 def InitLog():ROOT_DIR Path(__file__).resolve().parent.…

免费(daoban)gpt,同时去除广告

一. 内容简介 免费(daoban)gpt&#xff0c;同时去除广告&#xff0c;https://chat18.aichatos.xyz/&#xff0c;也可当gpt用&#xff0c;就是有点广告&#xff0c;大家也可以支持一下 二. 软件环境 2.1 Tampermonkey 三.主要流程 3.1 创建javascript脚本 点击添加新脚本 …

自己设计一个自动化测试框架

在进行自动化框架设计之前我们先来看两个问题&#xff0c;什么是自动化框架&#xff0c;设计的时候应该注意什么原则&#xff0c;然后该怎么做&#xff1f;本文会以一个web端的UI自动化测试框架设计为例 什么是自动化测试框架 什么是框架 特指为解决一个开放性问题而设计的具…

香港服务器不稳定的几种情况

​  近年来&#xff0c;随着互联网的迅猛发展&#xff0c;香港作为一个重要的网络枢纽地区&#xff0c;扮演着连接中国内地和国际网络的重要角色。一些用户表示在使用香港服务器时可能会遇到不稳定的情况&#xff0c;导致访问困难、加载缓慢甚至无法连接。 为什么香港服务器会…

uni-app 开发的H5 定位功能部署注意事项

一、H5部署的时候&#xff0c;如果设计到定位功能&#xff0c;需要注意以下几点 1、打包部署的时候需要在Web配置-定位和地图里面勾选一个地图&#xff0c;并配置key 2、打包部署需要域名是https协议的&#xff0c;大多数现代浏览器要求在HTTPS协议下才能够访问地理位置信息&a…

C/C++ system()函数的常用参数详解

文章目录 一、头文件二、system使用案例1. 执行系统命令2. 运行可执行程序3. 删除文件或目录4. 复制文件或目录5. 创建目录6. 网络操作7. 修改文件权限8. 查看系统信息9. 获取脚本结果在Linux操作系统下, system() 函数可以用来执行shell命令。你可以传递不同的命令字符串作为…

对于numpy.linalg和scipy.linalg(待完善)

这俩部分都是用于线性代数的计算&#xff0c;但是存在一些差别&#xff0c;下面是使用中出现的问题&#xff1a; 首先说明的是计算矩阵的伪逆的时候&#xff1a;np.linalg.pinv和scipy.linalg.pinv都是用于计算矩阵伪逆的&#xff0c;二者得到结果并不一致&#xff0c;只能说是…

如何快速使用Vue3在electron项目开发chrome Devtools插件

1、建立Vue项目 为了方便快速建立项目&#xff0c;我已经写好脚手架&#xff0c;直接clone项目&#xff0c;快速开发 点击快速进入源代码 拉取代码 git clone https://github.com/xygengcn/electron-devtool.git安装依赖 yarn运行项目 yarn dev打包项目 yarn build2、安装…

CentOS 搭建 Hadoop3 高可用集群

Hadoop FullyDistributed Mode 完全分布式 spark101spark102spark103192.168.171.101192.168.171.102192.168.171.103namenodenamenodejournalnodejournalnodejournalnodedatanodedatanodedatanodenodemanagernodemanagernodemanagerrecource managerrecource managerjob hist…

【Spring Boot】发送邮件功能

发送邮件功能 一.pom.xml文件添加邮件依赖二.发送邮件信息&#xff08;1&#xff09;固定配置在application.yml&#xff08;2&#xff09;发送邮箱配成活&#xff08;3&#xff09;底层发送邮件方法&#xff08;4&#xff09;QQ邮箱开通smtp服务&#xff08;5&#xff09;网易…

LeetCode每日一题——2103. Rings and Rods

文章目录 一、题目二、题解 一、题目 There are n rings and each ring is either red, green, or blue. The rings are distributed across ten rods labeled from 0 to 9. You are given a string rings of length 2n that describes the n rings that are placed onto the…

低代码平台如火如荼,告诉我它具体能做什么?

目录 一、前言 二、低代码平台 三、低代码平台的优劣 四、低代码能解决哪些问题&#xff1f; 五、好用且强大的低代码平台 六、结语 一、前言 目前低代码平台如火如荼。这一新兴技术为企业提供了一种高效、灵活、快速开发应用程序的方法&#xff0c;并在短时间内取得了巨大成功…

解决安装win11 23H2版本提示“此版本的windows不支持该处理器”

解决安装win11 23H2版本提示“此版本的windows不支持该处理器”的问题 按Win R 运行 regedit&#xff0c;打开注册表编辑器&#xff0c;定位到&#xff1a;计算机\HKEY_LOCAL_MACHINE\SYSTEM\Setup\MoSetup在该项下新建一个DWORD&#xff08;32位&#xff09;值&#xff0c;名…

如何使用 promise 和async、await 结合处理异步中的同步请求

文章目录 需求分析 需求 需求&#xff1a;让代码执行完循环A再进入代码B进行执行 分析 概念&#xff1a; 当我们谈论同步&#xff08;Synchronous&#xff09;和异步&#xff08;Asynchronous&#xff09;时&#xff0c;可以将其简单地理解为不同的任务处理方式。 同步操作…

体验SOLIDWORKS钣金切口工具增强 硕迪科技

在工业生产制造中&#xff0c;钣金加工是一种常用的加工方式&#xff0c;在SOLIDWORKS2024新版本中&#xff0c;钣金切口工具再次增强了&#xff0c;从SOLIDWORKS 2024 开始&#xff0c; 您可以使用切口工具在空心或薄壁圆柱体和圆锥体中生成切口。 只需在现有空心或薄壁圆柱体…

Vue监听事件

一、问题场景 项目有个需求&#xff0c;在登录页面&#xff0c;输入好账号密码后&#xff0c;直接可以点击回车就能够登录&#xff0c;效果和点击登录按钮一样&#xff0c;登录页面源码如下 <template><body id"poster"><el-form class"login-…