C++的类和new和delete和菱形继承机制

文章目录

  • 参考
  • 虚函数
  • 使用虚函数的class结构
  • 相关实现
  • 源码
  • IDA反编译
  • 子类虚表和父类虚表
  • 调用函数
  • 菱形继承

参考

https://showlinkroom.me/2017/08/21/C-%E9%80%86%E5%90%91%E5%88%86%E6%9E%90/
https://www.cnblogs.com/bonelee/p/17299985.html
https://xz.aliyun.com/t/5242?time__1311=n4%2BxnD07itGQ%3DD54AKDsA3xCuU%3DwQNY4D&alichlgref=https%3A%2F%2Fxz.aliyun.com%2Fu%2F15779#toc-3

虚函数

在C++中,虚函数(virtual function)是一种特殊的成员函数,它允许在派生类中重写或覆盖基类中定义的函数,从而实现多态性(polymorphism)。虚函数的关键特征和用途可以概括如下:

  1. 声明与定义
    要声明一个虚函数,需要在基类的成员函数声明前加上virtual关键字。例如:

    class Base {
    public:virtual void someFunction() {// 基类的实现}
    };
    

    派生类可以重写该函数,也可以不重写,如果重写,则在派生类中提供新的实现:

    class Derived : public Base {
    public:void someFunction() override {// 派生类的实现}
    };
    
  2. 虚函数表(VTable)
    实现虚函数的机制通常涉及到虚函数表(Virtual Table,简称vtable)。每个含有虚函数的类都会有一个虚函数表,表中存储着该类所有虚函数的地址。每个对象都有一个指向相应类虚函数表的指针(称为vptr)。当通过基类指针或引用调用虚函数时,程序会根据对象的vptr查找到正确的虚函数表,从而调用实际对象类型的函数实现。

  3. 纯虚函数与抽象类
    如果基类中的虚函数没有提供实现,并且在声明时被= 0初始化,那么这个函数被称为纯虚函数。含有纯虚函数的类不能实例化,只能作为基类被继承,这样的类称为抽象类。

使用虚函数的class结构

在这里插入图片描述

相关实现

在这里插入图片描述

  • new的本质,实际上就是malloc+构造函数
  • delete的本质就是析构函数+free
    在这里插入图片描述

源码

#include <iostream>
#include <string>using namespace std;class Person
{public:Person() {age = 0;name = "";};~Person() {};virtual void setName(string name) = 0;virtual void setAge(int age) = 0;
protected:int age;string name;};class Student :public Person {public:Student() {age = 17;}void setName(string name) {this->name = name;}string getName() {return this->name;}void setAge(int age) {if (age > 30 || age < 6) {cout << "not suitable for school!" << endl;return;}this->age = age;}int getAge() {return this->age;}
};int main() {Student* stu = new Student();stu->setName("Link");stu->setAge(18);cout << stu->getName() << " with age " << stu->getAge() << endl;return 0;
}

IDA反编译

在这里插入图片描述
子类构造函数
在这里插入图片描述
父类构造函数
在这里插入图片描述

子类虚表和父类虚表

在这里插入图片描述
纯虚函数的抽象类的虚表由__cxa_pure_virtual填充,有几个纯虚函数就有几个__cxa_pure_virtual,student类虚表分别有各自实现的虚函数,虚表上面是该类的typeinfo的地址
在这里插入图片描述

  • RTTI (Run-Time Type Information) 是C++的一项特性,允许程序在运行时识别对象的类型。
  • typeid运算符和dynamic_cast都是基于RTTI实现的。
  • 每个包含虚函数的类实例,在内存中都有一个隐藏的指针(称为vptr,虚函数指针),指向该类的虚函数表(vtable)。这个vptr是在对象创建时由编译器自动初始化的。
  • RTTI信息通常与vtable一起管理,其中可能包括类型描述信息和继承链信息等。
  1. Aclass* ptra = new Bclass;:这行代码创建了一个指向Bclass实例的指针,但这个指针被声明为Aclass*类型。因为BclassAclass的子类(假设如此),所以这是向上转型,是多态的基础。

  2. int ** ptrvf = (int**)(ptra);:这里进行了一次不安全的类型转换,将Aclass指针转换为了int**。这种转换通常是为了直接访问虚函数指针(vptr),因为在许多系统上,vptr是对象内存布局的第一个元素,可以被视为一个指针的指针。但是,请注意,这种做法非常危险且非标准,违反了类型安全原则。

  3. *((int*)ptrvf[0]-1):这一部分是为了找到RTTI信息的位置。ptrvf[0]访问的是第一个虚函数(按指针算起),减1则是尝试访问vptr前的一个位置,期望那里存放着RTTI相关数据的指针。这一步同样非常危险,依赖于特定编译器和平台的实现细节。

  4. *((RTTICompleteObjectLocator*)(...)):这里进一步将找到的地址强制转换为RTTICompleteObjectLocator指针,并解引用它。RTTICompleteObjectLocator是一个内部使用的结构(非标准公开接口),用于存储有关对象类型的完整信息,比如类的类型描述符、偏移量到类型名称字符串等。

调用函数

在这里插入图片描述
对于虚函数

  1. 先通过对象开始八个字节得到虚表地址
  2. 通过虚表地址和相关偏移得到函数地址
  3. 调用函数
    对于非虚函数
  4. 直接调用再text段实现的函数

菱形继承

#include<stdio.h>
#include<stdlib.h>//间接基类
class A {
public:virtual void function() {printf("A virtual function\n");}int a;
};//直接基类
class B :virtual public A { //虚继承
public:virtual void func() {printf("B virtual func()\n");}int b;
};//直接基类
class C :virtual public A { //虚继承
public:virtual void func() {printf("C virtual func()");}int c;
};//派生类
class D :public B, public C {
public:virtual void function() {printf("D virtual function()");}int d;
};int main(int argc, char** argv) {A* A_ptr = (A*)new D();A_ptr->function();return 0;
}

B和C继承A,D继承B和C

在这里插入图片描述通过虚表地址减去24后的位置的内容是32,然后再加上起始地址,对应到的V4为D内的A的对象内存位置,offset vbase应该是与虚基类的偏移也就是和A的偏移
在这里插入图片描述
在这里插入图片描述
这里给各个父类对象在本对象的空间的前八个字节赋值时,用的是本对象虚表对应到的不同父类的虚函数的地址
在这里插入图片描述

此时对应的虚表地址,所以**两次得到D::function的地址,然后参数为对象自己
在这里插入图片描述

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

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

相关文章

hadoop疑难问题解决_NoClassDefFoundError: org/apache/hadoop/fs/adl/AdlFileSystem

1、问题描述 impala执行查询&#xff1a;select * from stmta_raw limit 10; 报错信息如下&#xff1a; Query: select * from sfmta_raw limit 10 Query submitted at: 2018-04-11 14:46:29 (Coordinator: http://mrj001:25000) ERROR: AnalysisException: Failed to load …

Linux进程无法被kill

说明&#xff1a;记录一次应用进程无法被kill的错误&#xff1b; 场景 在一次导出MySQL数据时&#xff0c;使用下面的命令&#xff0c;将数据库数据导出为.sql文件&#xff0c;数据量大&#xff0c;导出时间长&#xff0c;于是我就将服务器重启了。 mysqldump -u username -…

springboot大学生就业管理系统-计算机毕业设计源码89344

摘 要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对大学生就业管理系统等问题&#xff0c;对大…

vs中C++项目中没有QT(.pro)文件怎么生成翻译ts文件

目录 使用 CMake 生成翻译文件 1.创建 CMakeLists.txt 文件 2.添加翻译生成规则 3.运行 CMake 4.生成翻译文件 使用命令行工具生成翻译文件 1.运行 lupdate 2.编辑 .ts 文件 3.运行 lrelease 网络上说的情况都是一个qt程序在VS中打开&#xff0c;拥有.pro文件的情况&a…

C++ 11【右值引用】

&#x1f493;博主CSDN主页:麻辣韭菜&#x1f493;   ⏩专栏分类&#xff1a;C修炼之路⏪   &#x1f69a;代码仓库:C高阶&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习更多C知识   &#x1f51d;&#x1f51d; 1.C 11 简介 目录 1.C 11 简介 2. 统一的列表…

Vue3【四】使用Vue2的写法写一个新的组件子组件和根组件

Vue3【四】使用Vue2的写法写一个新的组件 Vue3【四】使用Vue2的写法写一个新的组件 Vue3是向下兼容的&#xff0c;所有可以使用Vue的选项式写法 运行截图 目录结构 文件源码 App.vue <template><div class"app"><h1>你好世界! 我是App根组件<…

数据分析常用模型合集(二)RARRA模型、RFM模型

随着互联网的发展&#xff0c;前期平台的砸钱拉新、抢占市场&#xff0c;大家都叫AARRR小甜甜&#xff1b; 现在市场基本抢占得差不多&#xff0c;形成了一个平衡&#xff0c;新人基本拉不到多少&#xff0c;用户都知道干什么事有哪些平台&#xff0c;比如买东西主流淘宝、京东…

动态IP基础解析:为什么我们需要它?

在深入探讨互联网世界的运作机制时&#xff0c;IP地址无疑是其核心要素之一。IP地址&#xff0c;作为网络设备的唯一标识&#xff0c;不仅确保了数据的准确传输&#xff0c;更是网络安全和管理的基石。本文将深入解析动态IP的基础知识&#xff0c;并探讨其重要性及在多种场景下…

大语言模型应用与传统程序的不同

大语言模型&#xff08;LLM&#xff09; 被描述的神乎其神&#xff0c;无所不能&#xff0c;其实&#xff0c;大语言模型只是一个模型&#xff0c;它能够理解和生成自然语言&#xff0c;唯有依靠应用程序才能够发挥作用。例如&#xff0c;基于大模型可以构建一个最简单的会话机…

神经网络 torch.nn---Non-Linear Activations (ReLU)

ReLU — PyTorch 2.3 documentation torch.nn - PyTorch中文文档 (pytorch-cn.readthedocs.io) 非线性变换的目的 非线性变换的目的是为神经网络引入一些非线性特征&#xff0c;使其训练出一些符合各种曲线或各种特征的模型。 换句话来说&#xff0c;如果模型都是直线特征的…

拼多多面试:Netty如何解决粘包问题?

粘包和拆包问题也叫做粘包和半包问题&#xff0c;它是指在数据传输时&#xff0c;接收方未能正常读取到一条完整数据的情况&#xff08;只读取了部分数据&#xff0c;或多读取到了另一条数据的情况&#xff09;就叫做粘包或拆包问题。 从严格意义上来说&#xff0c;粘包问题和…

全球AI新闻速递6.5

全球AI新闻速递 1.昆仑万维&#xff1a;开源2 千亿稀疏大模型天工Skywork-MoE&#xff0c;首个支持单台RTX 4090服务器。 2.字节豆包推出桌面客户端&#xff1a;支持 Windows / macOS&#xff0c;快捷启动、AI 划词、 AI 搜索。 3.东风柳汽与优必选科技签署人形机器人汽车制造…

FreeRTOS手表项目多级菜单的实现

一、首先介绍一下智能手表项目的背景&#xff1a; 如图&#xff0c;关注焦点是任务&#xff1a; 1、在一个确定时刻&#xff0c;在那一圈任务中&#xff08;写有只有一个任务解挂&#xff09;只有一个任务处在运行&#xff0c;界面显示的是该任务应该显示的内容&#xff1b; …

vscode运行Java utf-8文件中文乱码报错

问题现象 vscode 运行utf-8 java文,爆出如下错误 hello.java:5: &#xfffd;&#xfffd;&#xfffd;&#xfffd;: &#xfffd;&#xfffd;&#xfffd;&#xfffd;GBK&#xfffd;IJ&#xfffd;&#xfffd;&#xfffd;ӳ&#xfffd;&#xfffd;&#xfffd;ַ&a…

如何增加网站外链?

想增加网站外链&#xff0c;无非就是去其他别的网站不停去发带自己网站链接的内容&#xff0c;这取决于你去什么平台发&#xff0c;一般来说发外链无非就是几种方式 博客以及论坛&#xff0c;要能提供评论功能的&#xff0c;在这种平台积极发表评论&#xff0c;并在允许的情况…

DevOps在数字化转型中的作用——实现数字化可视性

DevOps 的出现是为了满足不断增长的市场和消费者对技术应用程序的需求。它旨在在不牺牲软件质量的情况下创建更快的开发环境。DevOps 还专注于在快速开发生命周期中提高软件的整体质量。它依赖于多种技术、平台和工具的组合来实现所有这些目标。 容器化是一项彻底改变了我们开发…

如何在桌面添加多个便签 怎样在桌面创建便签

每当我坐在电脑前&#xff0c;总会被各种琐事和灵感所困扰&#xff0c;需要一个随手可记、随时可查的工具。直到我发现了敬业签便签软件彻底改变了我的工作习惯。 她不仅可以在桌面上轻松创建多个便签&#xff0c;而且它的“云游”功能简直让我爱不释手。想象一下&#xff0c;…

http://account.battlenet.com.cn

http://account.battlenet.com.cn 魔兽战网 短信验证 查了下&#xff0c;我老早以前账号还在&#xff0c;纪念下&#xff0c;少玩游戏。

计算机网络学习记录 应用层 Day6

你好,我是Qiuner. 为记录自己编程学习过程和帮助别人少走弯路而写博客 这是我的 github https://github.com/Qiuner ⭐️ ​ gitee https://gitee.com/Qiuner &#x1f339; 如果本篇文章帮到了你 不妨点个赞吧~ 我会很高兴的 &#x1f604; (^ ~ ^) 想看更多 那就点个关注吧 我…

国产方案|轮胎气压胎压计方案

气压胎压计的基本原理是利用气压传感器将气体气压转换为电信号&#xff0c;再通过电子芯片电路进行处理传输&#xff0c;再将这些信息转发给显示屏显示。常见的传感器包括模拟气压传感器和数字气压传感器。其中&#xff0c;模拟气压传感器是目前应用最广泛的传感器之一&#xf…