多态--下

news2024/11/27 0:43:02

文章目录

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

概念

优先使用组合、而不是继承;
继承会破坏父类的封装、因为子类也可以调用到父类的函数;
子类必须实现父类的纯虚函数;
内联函数没有地址,他是直接在定义的地方展开。所以不能是虚函数、虚函数是要有地址的;
虚函数不能是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.coloradmin.cn/o/1565181.html

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

相关文章

Springboot整合Milvus向量库

1. Milvus的Maven依赖&#xff0c; 配置如下 <dependency><groupId>io.milvus</groupId><artifactId>milvus-sdk-java</artifactId><version>2.3.4</version><exclusions><exclusion><artifactId>log4j-slf4j-imp…

【蓝桥备赛】异或和——树状数组、DFS

题目链接 异或和 思路分析 树上每个点都有一个点权&#xff0c;对树上的更新操作是修改指定点的点权&#xff0c;查询操作是查询指定点为根结点的子树点权异或和。 这里的这些操作都和树状数组的单点修改和区间查询非常相似&#xff0c;即我们在修改一个点时&#xff0c;同时…

浅谈物联网高速公路智慧配电室系统构建方案

关键词&#xff1a;高速公路&#xff1b;智慧供配电&#xff1b;电力监控&#xff1b;配电室智能运维托管&#xff1b;安全隐患 0、引言 随着高速公路事业的不断发展和路网的不断延伸&#xff0c;传统的管理方式已难以满足日益增长的需求&#xff0c;动态管理和安全隐患预警成…

当面试官问你插入排序算法,你敢说自己会吗?

算法学习的重要性 在程序员的世界里&#xff0c;算法就如同一座桥梁&#xff0c;连接着问题与解决方案&#xff0c;是实现优秀程序的关键。 掌握算法&#xff0c;就能够在面对各种问题时&#xff0c;找到最合适的解决方法&#xff0c;以最少的时间和空间&#xff0c;实现最优的…

【airtest】自动化入门教程(四)Poco元素定位

目录 一、基础操作 1、通过属性名等方式 2、通过属性组合 3、子节点方式 4、子节点加属性组合方式 5、孙节点offspring 6、兄弟节点sibling 7、父节点parent 8、正则表达式 9、直到某个元素出现 10、直到某个元素消失 二、通过局部坐标定位 1、使用局部坐标系的cli…

计算多个元素的累乘结果累乘器start默认初始为1 math.prod()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 计算多个元素的累乘结果 累乘器start默认初始为1 math.prod() [太阳]选择题 请问题目中的代码最后输出什么? import math list1 [1, 2, 3] print("【显示】list1 ",list1) pri…

留学生在美国大学利用AI工具到底算不算作弊呢?

自2022年以来&#xff0c;美国大学就开启了一场AI作弊与反作弊大战 战场小至测验&#xff0c;大至申请 这场战争并没有一方胜利&#xff0c;作弊者心思费尽 校方反作弊弄得教授们苦不堪言 那么作为中国留学生该如何避免这场战役呢&#xff1f; 毕竟还是学业要紧呢…… 故事…

[Leetcode笔记] 动态规划相关

前言 写题目写到了一些和动态规划相关的内容&#xff0c;所以在这里记录一下 LCR 089 题解思路 总的来说&#xff0c;就是用一个数组去存储当前的最优解&#xff0c;然后从0开始一路向上顺推过去&#xff0c;求得最后一位的最优解。 class Solution { public:int rob(vect…

【HTML】标签学习(下.2)

&#xff08;大家好哇&#xff0c;今天我们将继续来学习HTML&#xff08;下.2&#xff09;的相关知识&#xff0c;大家可以在评论区进行互动答疑哦~加油&#xff01;&#x1f495;&#xff09; 目录 二.列表标签 2.1 无序列表(重点) 2.2有序列表(理解) 2.3 自定义列表(重点…

数据结构算法系列----广度优先搜索(bfs)

目录 一、什么是bfs 二、bfs和dfs的差异 搜索顺序&#xff1a; 数据结构&#xff1a; 搜索方式&#xff1a; 三、bfs解决的主要问题 四、例题 一、什么是bfs BFS&#xff08;广度优先搜索&#xff09;是一种图搜索算法&#xff0c;用于在图或树数据结构中进行遍历。BFS从…

管理科学笔记

1.线性规划 画出区域&#xff0c;代入点计算最大最小值 2.最小生成树 a.断线法&#xff0c;从大的开始断 b.选择法&#xff0c;从小的开始选 3.匈牙利法 维度数量直线覆盖所有的0 4.一直选最当前路线最短路径 5.线性规划 6.决策论

反射——获取Class对象的三种方法,构造器、成员变量、方法

作用 学习如何获取类的信息&#xff0c;操作它们 获取class对象的三种方法 package com.zz.reflection;import com.zz.Interface.studentMannger.ClassManager;//目标&#xff1a;获取class对象 public class Test1Class {public static void main(String args[]) throws Class…

【Qt】使用Qt实现Web服务器(九):EventSource+JSON实现工业界面数据刷新

1、效果 效果如下,实时刷新温度、湿度 2、源码 2.1 index.html <html><body> <!-- 页面布局,本人对HTML标签不熟悉,凑合看吧 --> <div><label for

微信怎么恢复好友?7个方法助你轻松寻回失联好友

在数字化社交日益盛行的今天&#xff0c;微信作为我们日常生活中不可或缺的沟通工具&#xff0c;承载着与亲朋好友、同事伙伴之间的深厚情谊。然而&#xff0c;有时由于误操作或其他原因&#xff0c;我们可能会不小心删除了某些重要的微信好友&#xff0c;这时&#xff0c;如何…

【二叉树】Leetcode 105. 从前序与中序遍历序列构造二叉树【中等】

从前序与中序遍历序列构造二叉树 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 示例1&#xff1a; 输入: preorder [3,9,20,15,7], inorder …

刷题之动态规划-子数组

前言 大家好&#xff0c;我是jiantaoyab&#xff0c;开始刷动态规划的子数组类型相关的题目 动态规划5个步骤 状态表示 &#xff1a;dp数组中每一个下标对应值的含义是什么>dp[i]表示什么状态转移方程&#xff1a; dp[i] 等于什么1 和 2 是动态规划的核心步骤&#xff0c;…

深度学习理论基础(五)卷积神经网络CNN

目录 前述&#xff1a;卷积神经网络基础1.卷积网络流程2.卷积网络核心3.卷积下采样4.卷积上采样--转置卷积 一、卷积神经网络层1.卷积层&#xff08;1&#xff09;内部参数&#xff1a;卷积核权重&#xff08;2&#xff09;内部参数&#xff1a;偏置&#xff08;3&#xff09;外…

dm8 开启归档模式

dm8 开启归档模式 1 命令行 [dmdbatest1 dm8]$ disql sysdba/Dameng123localhost:5237服务器[localhost:5237]:处于普通打开状态 登录使用时间 : 3.198(ms) disql V8 SQL> select name,status$,arch_mode from v$database;行号 NAME STATUS$ ARCH_MODE ----------…

DETR【Transformer+目标检测】

End-to-End Object Detection with Transformers 2024 NVIDIA GTC&#xff0c;发布了地表最强的GPU B200&#xff0c;同时&#xff0c;黄仁勋对谈《Attention is All You Need》论文其中的7位作者&#xff0c;座谈的目的无非就是诉说&#xff0c;Transformer才是今天人工智能成…