多态--下

news/2024/4/30 4:04:48

文章目录

      • 概念
      • 多态如何实现的指向谁调谁?
        • 例子
        • 分析
      • 含有虚函数类的大小是多少?
      • 虚函数地址
      • 虚表地址
      • 多继承的子类的大小怎么计算?
      • 练习题
      • 虚函数和虚继承

概念

优先使用组合、而不是继承;
继承会破坏父类的封装、因为子类也可以调用到父类的函数;
子类必须实现父类的纯虚函数;
内联函数没有地址,他是直接在定义的地方展开。所以不能是虚函数、虚函数是要有地址的;
虚函数不能是static函数,因为没有this指针、等于说是全局的,虚表要的是对象里面的;
普通函数继承是实现继承,就是不去重写父类的函数体,直接用的;虚函数的继承目的就是要重写这个函数体,自改成自己想要的;
虚函数在编译阶段就生成了,并且存在代码段;

析构函数最好写成多态。多态就是指向谁调谁,这样子类就可以调用自己的析构函数
构造函数不能写成多态;因为对象中的虚表指针实在初始化列表时开始初始化的;

多态如何实现的指向谁调谁?

虚函数的地址存在虚函数表中,通过虚函数表来调用虚函数;
同类型的对象共用一个虚表;
子类不重写父类的虚函数,也是和父类共用同一张虚表;重写就不会共用虚表

例子
#define _CRT_SECURE_NO_WARNINGS#include <iostream>
using namespace std;class person
{
public:virtual void BuyTicket(){cout << "成人票" << endl;}
};class student : public person
{
public:virtual void BuyTicket(){cout << "学生票" << endl;}
};void buy(person& b)
{b.BuyTicket();
}int main()
{student s;person p;buy(s);buy(p);return 0;
}
分析

1、子类继承后,虚函数表是一样的,重写后就把虚函数表copy出来换一个地址,把新的内容覆盖了;
image.png
2、调用时传过去的参数累心是谁的就指向谁的虚函数表,s是student类型那就是用student里面的函数;
image.png
2、子类虚函数不重写会怎样
如果不重写,那父级和子级虚函数表的地址是相同的
image.png

含有虚函数类的大小是多少?

普通函数不占空间,虚函数有个指针、所以站指针的大小空间,32位的4字节、64位的8字节;
在类里面:虚函数占一个指针的大小,不管有多少个虚函数就只占一个指针的大小;对象里面只是存了一个指针指向这个虚表
虚表的大小才要看有几个虚函数;
多继承的子类,继承了几个父类就有几个父类的指针大小
如下就一个虚函数64位
image.png
计算方法和结构体其实一样,内存对齐
加了一个int变量,理应8+4 = 12,但是最小对齐数为8,类的大小是其成员最小对齐数的整数倍;所以是16;
image.png

虚函数地址

#define _CRT_SECURE_NO_WARNINGS#include <iostream>
using namespace std;class person
{
public:virtual void BuyTicket(){cout << "成人票" << endl;}
private:int a;
};class student : public person
{
public:virtual void BuyTicket(){cout << "学生票" << endl;}
};void buy(person& b)
{b.BuyTicket();
}class base
{
public:virtual void func1(){cout << "base::func1" << endl;}
private:int _b = 1;
};void func()
{person b1;printf("vftptr:%p\n", *(int*) & b1);int i = 0;int* p1 = &i;int* p2 = new int;const char* p3 = "sad";printf("栈变量:%p\n", p1);printf("堆变量:%p\n", p2);printf("代码段常量:%p\n", p3);printf("代码段函数地址:%p\n", &base::func1);}int main()
{student s;person p;buy(s);buy(p);int size = sizeof(s);cout << size << endl;func();return 0;
}

问题:虚函数存在哪?虚函数表存在哪?
答:虚函数和普通函数都存在代码段、虚函数表也存在代码段
image.png
普通的函数、函数名就是地址(首地址)
成员函数的地址要加取地址符号&,以及要指定属于哪个类,也就是这样 &类名::函数名

虚表地址

class Base
{
public:virtual void fuc1(){cout << "fuc1" << endl;}virtual void fuc2(){cout << "fuc2" << endl;}
private:int a;
};class derive : public Base
{
public:virtual void fuc1(){ cout << "fuc1111" << endl; }virtual void fuc3(){cout << "fuc3" << endl;}
};typedef void(*VF_PTR)();//函数指针类型怎么写的
void PrintVFTable(VF_PTR* pTable)
{for(size_t i = 0; pTable[i] != 0; ++i){printf("vfTable[%d]:%p->", i, pTable[i]);VF_PTR f = pTable[i];f();//运行函数}cout << endl;
}int main()
{//student s;//person p;//buy(s);//buy(p);//int size = sizeof(s);//cout << size << endl;//func();derive a;Base b;PrintVFTable((VF_PTR*)(*(int*)&b));PrintVFTable((VF_PTR*)(*(int*)&a));return 0;
}

父类上面两行、子类为下面三行;
子类fuc1重写过了虚表地址就变了,没写fuc2但是继承了fuc2的虚表地址;
image.png
强转两次,(int*)是要取前4个字符,(VF_PTR*)强转函数指针
image.png
函数指针怎么写 void(*)()
type void(*a)(),这个a就代表函数指针了

多继承的子类的大小怎么计算?

继承几个父类就含有几个指针;

class Base1
{
public:virtual void fuc1() { cout << "b1:fuc1" << endl; }virtual void fuc2() { cout << "b1:fuc2" << endl; }
private:int a1;
};class Base2
{
public:virtual void fuc1() { cout << "b2:fuc1" << endl; }virtual void fuc2() { cout << "b2:fuc2" << endl; }
private:int a2;
};class derive2 : public Base1, public Base2
{virtual void fuc1() { cout << "b2:fuc1" << endl; }virtual void fuc3() { cout << "b2:fuc2" << endl; }private:int a3;
};int main()
{//student s;//person p;//buy(s);//buy(p);//int size = sizeof(s);//cout << size << endl;//func();//derive a;//Base b;//PrintVFTable((VF_PTR*)(*(int*)&b));//PrintVFTable((VF_PTR*)(*(int*)&a));cout << sizeof(derive2) << endl;derive2 a1;PrintVFTable((VF_PTR*)(*(int*)&a1));PrintVFTable((VF_PTR*)(*(int*)((char*)&a1 + sizeof(Base1))));return 0;
}

image.png
继承两个父类,两个指针的大小;
不同父类的虚表继承后不会共用;
derive2成员函数fuc3的虚表和Base1的虚表共用了,随机共用一个父类的虚表;

练习题

1、
image.png
2、
类中就只有变量、按顺序往下排;
p3是指向整块区域,所以首地址也是b1存的首地址;
p1也是b1存的首地址;
p2是b2的首地址;
所以选c;
image.png

虚函数和虚继承

image.png
是两个完全不同的概念;
用的都是virtual;

虚继承
image.png
菱形继承时,B、C继承A,D继承BC;

如上图使用虚继承,就会把公共的变量_a,放到一个地址上;
如果不用虚继承的话,就会有三个_a的地址;造成数据的冗余性;
二义性也是不用虚继承造成的,就是再回去找_a时,有很多_a;

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

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

相关文章

小林coding图解计算机网络|基础篇01|TCP/IP网络模型有哪几层?

小林coding网站通道&#xff1a;入口 本篇文章摘抄应付面试的重点内容&#xff0c;详细内容还请移步&#xff1a; 文章目录 应用层(Application Layer)传输层(Transport Layer)TCP段(TCP Segment) 网络层(Internet Layer)IP协议的寻址能力IP协议的路由能力 数据链路层(Link Lay…

HTML——4.表格、列表、区块

一、表格 HTML 表格是用于展示结构化数据的重要元素&#xff0c;它允许将数据以行和列的形式组织和显示。 基本结构和常见元素&#xff1a; 1. <table> 元素 <table> 元素是 HTML 表格的根元素&#xff0c;它用于定义整个表格的开始和结束。 2. <thead>、…

BetterZip for Mac2024最新mac解压缩软件

作为一名软件专家&#xff0c;对于市面上各类软件都有较为深入的了解&#xff0c;下面介绍的是一款适用于Mac系统的解压缩软件——BetterZip&#xff0c;将从其功能特点、使用方法、用户体验及适用人群等方面进行详细介绍。 BetterZip5-安装包绿色版下载如下&#xff1a; htt…

ARM FVP平台的terminal窗口大小如何设置

当启动ARM FVP平台时&#xff0c;terminal窗口太小怎么办&#xff1f;看起来非常累眼睛&#xff0c;本博客来解决这个问题。 首先看下ARM FVP平台对Host主机的需求&#xff1a; 通过上图可知&#xff0c;UART默认使用的是xterm。因此&#xff0c;我们需要修改xterm的默认字体设…

最新AI智能系统ChatGPT网站源码V6.3版本,GPTs、AI绘画、AI换脸、垫图混图+(SparkAi系统搭建部署教程文档)

一、前言 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;那么如何搭建部署AI创作ChatGPT&#xff1f;小编这里写一个详细图文教程吧。已支持GPT…

JavaEE 初阶篇-生产者与消费者模型(线程通信)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 生产者与消费者模型概述 2.0 在生产者与消费者模型中涉及的关键概念 2.1 缓冲区 2.2 生产者 2.3 消费者 2.4 同步机制 2.5 线程间通信 3.0 实现生产者与消费者模…