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的地址,然后参数为对象自己
在这里插入图片描述

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

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

相关文章

【操作与配置】MySQL安装及启动

【操作与配置】MySQL安装及启动 下载MySQL 进入官网&#xff0c;选择社区版下载 在windows安装 选择不登陆下载 安装MySQL 双击官方安装包 选择“Developer Default”&#xff08;默认&#xff09;即可 Execute&#xff0c;安装完成后next TCP/IP端口等&#xff0c;默认即可…

ArrayList集合

Java学习笔记&#xff08;新手纯小白向&#xff09; 第一章 JAVA基础概念 第二章 JAVA安装和环境配置 第三章 IntelliJ IDEA安装 第四章 运算符 第五章 运算符联系 第六章 判断与循环 第七章 判断与循环练习 第八章 循环高级综合 第九章 数组介绍及其内存图 第十章 数…

nginx动静分离和反向代理

一、动静分离 动静分离指的是将动态内容和静态内容分开处理。动态内容通常由后端应用程序生成&#xff0c;例如PHP、Python或Node.js&#xff0c;静态内容则包括图片、CSS、JavaScript等文件。 例子&#xff1a; #代理服务器一 server{listen 80;server_name www.dj.com;r…

Qt如何让按钮的菜单出现在按钮的右侧

直接上代码&#xff0c;我们用到了一个eventfilter的函数功能。这个函数比较厉害和重要&#xff0c;大家务必经常拿出来看看。 void MainWindow::initMenu() { QMenu* menuLiXiang new QMenu; QAction* actXiangMuZhangCheng new QAction("项目章程"); …

BERT+PET方式模型训练

基于BERTPET方式文本分类模型搭建 模型搭建 本项目中完成BERTPET模型搭建、训练及应用的步骤如下&#xff08;注意&#xff1a;因为本项目中使用的是BERT预训练模型&#xff0c;所以直接加载即可&#xff0c;无需重复搭建模型架构&#xff09;: 一、实现模型工具类函数二、实现…

QT获取最小化,最大化,关闭窗口事件

QT获取最小化&#xff0c;最大化&#xff0c;关闭窗口事件 主程序头文件&#xff1a; 实现&#xff1a; changeEvent&#xff0c;状态改变事件 closeEvent触发点击窗口关闭按钮事件 其代码它参考&#xff1a; /*重写该函数*/ void MainWindow::changeEvent(QEvent *event) {…

python 字符串(str)、列表(list)、元组(tuple)、字典(dict)

学习目标: 1:能够知道如何定义一个字符串; [重点] 使用双引号引起来: 变量名 "xxxx" 2:能够知道切片的语法格式; [重点] [起始: 结束] 3:掌握如何定义一个列表; [重点] 使用[ ]引起来: 变量名 [xx,xx,...] 4:能够说出4个列表相关的方法; [了解] ap…

kafka学习笔记 @by_TWJ

目录 1. 消息重复消费怎么解决1.1. 确保相同的消息不会被重复发送(消费幂等性)1.2. 消息去重1.3. 消息重试机制1.4. kafka怎么保证消息的顺序性1.4.1. 利用分区的特征&#xff1a;1.4.2. 解决办法&#xff1a;1.4.3. 分区分配策略1.4.3.1. RangeAssignor &#xff08;每组(Topi…

水电表自动抄表系统

1.简述 水电表自动抄表系统是一种现代化智能化管理系统&#xff0c;它利用先进的物联网&#xff0c;完成了远程控制、即时、零接触的水电表读值收集&#xff0c;大大提升了公共事业服务项目的效率和准确性。该系统不仅减少了人工抄表工作量&#xff0c;还避免了人为失误&#…

Mac环境下,简单反编译APK

一、下载jadx包 https://github.com/skylot/jadx/releases/tag/v1.4.7 下载里面的这个&#xff1a;下载后&#xff0c;找个干净的目录解压&#xff0c;我是放在Downloads下面 二、安装及启动 下载和解压 jadx&#xff1a; 下载 jadx-1.4.7.zip 压缩包。将其解压到你希望的目…

Flink的简单学习四

一 有状态计算 1.1 概念 1.状态;上一次计算的结果 2.需要基于上一个结果来进行计算&#xff0c;被称为有状态计算 1.2 未使用有状态计算 1.下面这个代码将相同的key发送到同一个task任务里面计算。就是因为这个导致了&#xff0c;明明之前没有输入b&#xff0c;但是输入b之…

【鸿蒙】开发之页面跳转组件—实现页面跳转方法汇总!

①不同 Slice 间跳转&#xff0c;同一个 Ability 中&#xff0c;优点是方便&#xff0c;高效&#xff0c;缺点是业务逻辑复杂度受限。 button.setClickedListener(listener -> present(new SecondAbilitySlice(), new Intent()) );②使用 Intent 借助于 ElementName&#x…

【管理咨询宝藏126】德勤咨询为某大型机械集人才体系发展思路方案

本报告首发于公号“管理咨询宝藏”&#xff0c;如需阅读完整版报告内容&#xff0c;请查阅公号“管理咨询宝藏”。 【管理咨询宝藏126】德勤咨询为某大型机械集人才体系发展思路方案 【格式】PDF版本 【关键词】人力咨询、人才体系、人才盘点 【核心观点】 - 中国整车与零部件…

七天进阶elasticsearch[two]

批量保存 批量保存是通过_bulk API来实现的 请求方式 post 请求地址 _bulk 通过_bulk操作文档,一般至少有两行参数 第一行用于确定要干什么(插入,修改还是删除) 第二行才是操作的数据; 当然以上是标准操作,也可以不遵循标准操作,使用不同的请求方式来完成 批量保存demo…

故障预警 vs 故障分类:哪个更有意义,哪个更具挑战性?

故障预警 vs 故障分类&#xff1a;哪个更有意义&#xff0c;哪个更具挑战性&#xff1f; 在现代工业系统中&#xff0c;风力发电机、制造设备等关键装置的可靠性和稳定性对生产效率至关重要。为此&#xff0c;故障预警和故障分类成为保障设备正常运行的重要手段。那么&#xf…

备份和恢复realme智能手机:综合指南

realme自2018年成立至今&#xff0c;一直秉持着“敢于超越”的品牌精神&#xff0c;专注于为全球年轻用户提供性能卓越、设计新颖的高品质手机。对于如何备份和恢复realme手机&#xff0c;本文将介绍多种不同的方法。 第1部分&#xff1a;使用Coolmuster Android Backup Mana…

怎解ESP-ADF组件 error: unknown type name ‘xSemaphoreHandle‘

没有定义&#xff0c;看一下最上面的头文件。 通过看最上面的头文件引入&#xff0c;可以看到信号量头文件已经有了&#xff0c;那很明显就是类型的兼容的问题&#xff0c;打开MenuConfig开启向后兼容API的选项。 问题解决。

CVE-2024-2961:将phpfilter任意文件读取提升为远程代码执行(RCE)

0x00 前言 前几天p牛师傅在星球发了一个帖子&#xff1a;PHP利用glibc iconv()中的一个缓冲区溢出漏洞CVE-2024-2961&#xff0c;实现将文件读取提升为任意命令执行漏洞&#xff0c;当时觉得这个漏洞蛮有意思&#xff0c;就想研究一下。于是web狗开启了一次二进制漏洞的学习之…

输出有10个元素的整型数组各元素的值

&#xff08;1&#xff09;下标法 编写程序&#xff1a; &#xff08;2&#xff09;指针法&#xff1a; 将上面程序第7行和第10行的a[i]改为"*(ai)"。 &#xff08;3&#xff09;用指针变量指向数组元素 编写程序&#xff1a; 运行结果&#xff1a; 对3种方法的比…

家政上门按摩小程序源码 仿东郊到家小程序源码

家政上门按摩小程序源码 仿东郊到家小程序源码 实用行业 适用于&#xff1a;预约私教&#xff0c;预约瑜伽/健身、预约美容/美发/美甲、预约理疗/足疗/推拿、预约清洁/保洁/保安、预约洗车/维修/安装、预约保姆/月嫂/护工/洗衣/烧饭、钟点工等暖心服务。在家政市场上&#xf…