【C++】多态结束篇

news2024/10/7 12:27:00

在这里插入图片描述

欢迎来到Cefler的博客😁
🕌博客主页:那个传说中的man的主页
🏠个人专栏:题目解析
🌎推荐文章:题目大解析(3)

在这里插入图片描述


目录

  • 👉🏻虚表存在内存中哪里?
  • 👉🏻虚函数的地址一定会被放进类的虚函数表当中吗?
  • 👉🏻多态的一些问答题
  • 👉🏻多态选择题

👉🏻虚表存在内存中哪里?

下面一段代码来验证查看验证一下:

#include <iostream>
using namespace std;
class Base
{
public:
	virtual void  func1() { cout << "Base::func1()" << endl; }
	virtual void func2(){ cout << "Base::func2()" << endl; }
};
class Derive:public Base
{
public:
	virtual void  func1() { cout << "Derive::func1()" << endl; }
	virtual void  func3() { cout << "Derive::func3()" << endl; }
	virtual void  func4() { cout << "Derive::func4()" << endl; }

};

int main()
{
	Base b1;
	static int a = 0;//静态区
	int b = 0;//栈区
	int* p1 = new int;//堆区
	const char* p2 = "hello";//代码段/字符常量区
	printf("静态区:%p\n", &a);
	printf("栈:%p\n", &b);
	printf("堆区:%p\n", p1);
	printf("代码段:%p\n", p2);
	printf("虚表:%p\n", *((int*)&b1));


	return 0;
}

在这里插入图片描述
这里我们从地址上来看,可以得出一个大概的结论:虚表应该是存放在常量区中的,这个也好理解,虚表创建之后,本身就不让再修改了。

这里要解释一下这里打印虚表指针的方式

*((int*)&b1)

为什么要这样打印呢?实际上我们知道,b1对象头4字节中存储着虚表指针(因为指针大小为4字节),那么我们如何取出这4字节的指针呢?
如果直接取b1的地址并进行解引用,根本不知道要取多大的字节范围。
回顾指针知识,我们对一个int 类型的数组进行元素解引用使用,靠的就是int类型这个指针偏移量,让我们知道每次指针解引用的大小范围是多少。
所以这里同理,我们可以对b1的地址进行强制类型转为为int*.
这样我们对一个类型为int*类型的地址解引用,自然就只会取到4字节大小的内容。
在这里插入图片描述

👉🏻虚函数的地址一定会被放进类的虚函数表当中吗?

下面一段代码来验证查看验证一下:

#include <iostream>
using namespace std;
class Base
{
public:
	virtual void  func1() { cout << "Base::func1()" << endl; }
	virtual void func2(){ cout << "Base::func2()" << endl; }
};
class Derive:public Base
{
public:
	virtual void  func1() { cout << "Derive::func1()" << endl; }
	virtual void  func3() { cout << "Derive::func3()" << endl; }
	virtual void  func4() { cout << "Derive::func4()" << endl; }

};
class X :public Derive {
public:
	virtual void func3(){ cout << "X::func3()" << endl; }
};
typedef void (*VFUC)();//声明一个函数指针类型
void printVFT(VFUC a[])
{
	for (int i = 0; a[i]!=nullptr; i++)
	{
		printf("[%d]:%p->",i, a[i]);
		VFUC f = a[i];
		f();//(*f)()调用函数也可以
	}
}
int main()
{
	Base b;
	printVFT((VFUC*)*((int*)&b));
	cout << endl;
	Derive d;
	printVFT((VFUC*)*((int*)&d));
	cout << endl;

	X x;
	printVFT((VFUC*)*((int*)&x));



	return 0;
}

在这里插入图片描述
所以得出结论:虚函数的地址一定会被放进类的虚函数表当中

多重继承的话,会有多个虚表
在这里插入图片描述

👉🏻多态的一些问答题

  1. inline函数可以是虚函数吗?
    答: 可以,不过多态调用编译器就忽略inline属性,这个函数就不再是inline,因为虚函数要放到虚表中去

  2. 静态成员可以是虚函数吗?
    答:不能,因为静态成员函数没有this指针,使用类型::成员函数的调用方式无法访问虚函数表,所以静态成员函数无法放进虚函数表。

  3. 构造函数可以是虚函数吗?
    答:不能,因为对象中的虚函数表指针是在构造函数初始化列表阶段才初始化的。

  4. 对象访问普通函数快还是虚函数更快?
    答:首先如果是普通对象,是一样快的。如果是指针对象或者是引用对象,则调用的普通函数快,因为构成多态,运行时调用虚函数需要到虚函数表中去查找

  5. 虚函数表是在什么阶段生成的,存在哪的?
    答:虚函数表是在编译阶段就生成的,一般情况下存在代码段(常量区)的。

👉🏻多态选择题

一、
关于虚表说法正确的是( D)
A.一个类只能有一张虚表
B.基类中有虚函数,如果子类中没有重写基类的虚函数,此时子类与基类共用同一张虚表
C.虚表是在运行期间动态生成的
D.一个类的不同对象共享该类的虚表
解析
A.多继承的时候,就会可能有多张虚表

B.父类对象的虚表与子类对象的虚表没有任何关系,这是两个不同的对象

C.虚表是在编译期间生成的

D.一个类的不同对象共享该类的虚表

二、

以下程序输出结果是( )

class A

{

public:

  A ():m_iVal(0){test();}

  virtual void func() { std::cout<<m_iVal<<‘ ’;}

  void test(){func();}

public:

  int m_iVal;

};



class B : public A

{

public:

  B(){test();}

  virtual void func()

  {

    ++m_iVal;

    std::cout<<m_iVal<<‘ ’;

  }

};



int main(int argc ,char* argv[])

{

  A*p = new B;

  p->test();

  return 0;

}

结果是:0 1 2

解析:new B时先调用父类A的构造函数,执行test()函数,在调用func()函数,由于此时还处于对象构造阶段,多态机制还没有生效,所以,此时执行的func函数为父类的func函数,打印0,构造完父类后执行子类构造函数,又调用test函数,然后又执行func(),由于父类已经构造完毕,虚表已经生成,func满足多态的条件,所以调用子类的func函数,对成员m_iVal加1,进行打印,所以打印1, 最终通过父类指针p->test(),也是执行子类的func,所以会增加m_iVal的值,最终打印2, 所以答案为C 0 1 2

三、
假设A类中有虚函数,B继承自A,B重写A中的虚函数,也没有定义任何虚函数,则(B )
A.A类对象的前4个字节存储虚表地址,B类对象前4个字节不是虚表地址
B.A类对象和B类对象前4个字节存储的都是虚表的地址
C.A类对象和B类对象前4个字节存储的虚表地址相同
D.A类和B类中的内容完全一样,但是A类和B类使用的不是同一张虚表

解析
A.父类对象和子类对象的前4字节都是虚表地址

B.A类对象和B类对象前4个字节存储的都是虚表的地址,只是各自指向各自的虚表

C.不相同,各自有各自的虚表

D.A类和B类不是同一类内容不同

四、

下面函数输出结果是(A )

class A

{

public: 

  virtual void f()

  {

    cout<<"A::f()"<<endl;

  }

};



class B : public A

{

private:

   virtual void f()

  {

    cout<<"B::f()"<<endl;

  }

};



A* pa = (A*)new B;

pa->f();

A.B::f()
B.A::f(),因为子类的f()函数是私有的
C.A::f(),因为强制类型转化后,生成一个基类的临时对象,pa实际指向的是一个基类的临时对象
D.编译错误,私有的成员函数不能在类外调用

解析:
A.正确

B.虽然子类函数为私有,但是多态仅仅是用子类函数的地址覆盖虚表,最终调用的位置不变,只是执行函数发生变化

C.不强制也可以直接赋值,因为赋值兼容规则作出了保证

D.编译正确


如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

SpringBoot中的日志使用

SpringBoot的默认使用 观察SpringBoot的Maven依赖图 可以看出来&#xff0c;SpringBoot默认使用的日志系统是使用Slf4j作为门户&#xff0c;logback作为日志实现 编写一个测试代码看是否是这样 SpringBootTest class SpringbootLogDemoApplicationTests {//使用Slf4j来创建LOG…

广西建筑模板的材质类型和特点有哪些?

广西建筑模板常用的材质类型包括木模板、钢模板、竹胶合板、塑料模板和铝合金模板等。每种材质都具有不同的特点和适用范围。 1. 木模板&#xff1a; - 适用于高层建筑的水平模板、剪力墙、竖向墙板、高架桥、立交桥、大坝、隧道施工及梁柱模板。 - 具有高强度和良好的韧性&…

通过全流量分析助力某医院关键业务提高性能

背景 福建某大型医院信息科近期接到多人反应&#xff0c;业务系统访问慢和报错情况&#xff0c;因为问题出现没有时间和操作规律&#xff0c;网络管理员通过多种方式排查&#xff0c;未能得到有效的原因定位。 我们已将NetInside流量分析系统部署到医院的机房内&#xff0c;使用…

2022年京东双十一手机数码全品类数据回顾

2023年双十一临近&#xff0c;特此带大家回顾一下去年双十一热门品类的一些战况数据。这一期是京东手机电脑数码。 整体表现来看&#xff0c;2022年双11大促京东手机、电脑、数码类产品并没有想象中的增长状态&#xff0c;无论是电脑中的笔记本、数码中的相机&#xff0c;或者是…

InnoDB事务

1. 支持的事务 扁平事务&#xff1a;所有操作都处于同一层次 带保持点的扁平事务&#xff1a;事务能够回到保持点的状态。 链事务&#xff1a;系统崩溃时&#xff0c;所有保存点都将消失。 嵌套事务&#xff1a;具有层次结构&#xff08;树&#xff09;。任意一个事务回滚会…

c语言进制的转换二进制转换10进制

c语言进制的转换之二进制转换10进制 c语言的进制的转换 c语言进制的转换之二进制转换10进制一、二进制转换10进制的方法二、10进制程序打印 一、二进制转换10进制的方法 二进制&#xff1a; 二进制逢二进一&#xff0c;所有的数组是0、1组成 十进制转二进制&#xff1a; 除二反…

AD20~PCB的板层设计和布线

1、打开51单片机最小系统的工程文件。 2、完成原理图后续工作&#xff1a;打开原理图文件&#xff0c;双击元件“CH340X”窗口右边弹出元件内部属性设置界面&#xff0c;在窗口下方点击“Footprint ->Add…”按钮进入添加元件类型界面&#xff0c;进入元件封装选择界面&…

2023年行云绽放傲冠股份厨艺比拼团建活动圆满结束

十月金秋&#xff0c;阳光灿烂&#xff0c;碧空如洗。 为了促进员工之间更好的交流&#xff0c;激发员工阳光向上的心态&#xff0c;充满活力&#xff0c;拥抱自然&#xff0c;深圳市行云绽放科技有限公司及深圳市傲冠软件股份有限公司于2023年10月13日组织深圳总部员工自驾出…

接口自动化测试工具大全

在互联网时代&#xff0c;服务端测试已经成为一个重要的产品保障手段&#xff0c;各对此公司实施的方法和技术也不同&#xff0c;本文我们就来讨论一下。 互联网服务端接口自动化是各个公司都需要一部分业务&#xff0c;如何快速高效地完成接口测试呢&#xff1f; 以帮助大家实…

Linux基础命令1——Linux的命令格式与命令分类

目录 Linux命令格式 Linux命令分类 如何判断命令的类型——Type命令 内置命令 外部命令 alias命令 命令的执行效率与过程 Linux命令格式 命令格式 完整的命令格式分为三部分&#xff1a;命令、参数、对象 其中命令与参数、参数与参数、参数与对象之间最少要有一个空格做…

变分贝叶斯深度学习综述

**©PaperWeekly 原创 作者 |**薛博阳 **单位 |**香港中文大学 **研究方向 |**语言模型 引言 近年来&#xff0c;贝叶斯深度学习&#xff08;Bayesian Deep Learn-ing&#xff09;在诸多领域得到广泛关注应用&#xff0c;效果显著。本文将针对贝叶斯深度学习框架进行系…

​如何使用ArcGIS Pro制作一张地形图

01数据来源 本教程所使用的数据是从水经微图中下载的DEM数据&#xff0c;除了DEM数据&#xff0c;常见的GIS数据都可以从水经微图中下载&#xff0c;你可以通过关注“水经注GIS”&#xff0c;然后在后台回复“微图”即可获取软件下载地址&#xff0c;当然也可以直接在水经注…

TensorFlow2从磁盘读取图片数据集的示例(tf.data.Dataset.list_files)

import os import warnings warnings.filterwarnings("ignore") import tensorflow as tf from tensorflow.keras.optimizers import Adam from tensorflow.keras.applications.resnet import ResNet50 from pathlib import Path import numpy as np#数据所在文件夹 …

AI爆文变现脚本:0基础小白的保姆级操作教程-更新迭代

脚本作用&#xff1a;这个脚本主要是辅助训练营的同学使用的&#xff0c;脚本可以增加发文的效率。 脚本现在已经更新了9个版本了。目的是为了更方便大家操作使用。 AI爆文流量主(广告)变现项目的实际操作教程&#xff0c;我之前分享过了&#xff0c;大家感兴趣的可以再去看看…

灰色和测试环境打包串台

事情是这样的&#xff1a; 最近开发总说jenkins灰色环境打包总是到成测试环境的&#xff0c;测试环境总是走到了线上了。我们排查了也很久最终发现原来是这个问题导致的。如下&#xff1a; 修改如下&#xff1a; 问题解决

Tomcat+nginx负载均衡和动静分离

Nginx实现负载均衡和动静分离的原理 Nginx实现负载均衡是通过反向代理实现Nginx服务器作为前端&#xff0c;Tomcat服务器作为后端&#xff0c;web页面请求由Nginx服务来进行转发。 但是不是把所有的web请求转发&#xff0c;而是将静态页面请求Ncinx服务器自己来处理&#xff0c…

当年很流行,现在已经淘汰的前端技术有哪些?

近几年&#xff0c;前端技术真可谓是飞速发展&#xff0c;不断有新的技术涌现&#xff0c;爆火的前端框架 Astro&#xff0c;前端运行时 Bun&#xff0c;构建工具 Vite 等都给前端提供了强大动力。当然&#xff0c;也有很多前端技术随着技术的发展不再需要使用&#xff0c;有了…

【数据结构】线性表(十一)队列:双端队列及其基本操作(初始化、判空、判满、头部入队、尾部入队、头部出队、尾部出队、存取队首队尾元素)

文章目录 一、队列1. 定义2. 基本操作 二、顺序队列三、链式队列双端队列0. 头文件1. 队列结构体2. 初始化3. 判断队列是否为空4. 判断队列是否已满5. 头部入队6. 尾部入队7. 头部出队8. 尾部出队9. 存取队列头部的元素10. 存取队列尾部的元素11. 释放队列内存12. 主函数13. 代…

每日一题 2678. 老人的数目(简单)

简单题&#xff0c;不多说 class Solution:def countSeniors(self, details: List[str]) -> int:ans 0for l in details:if int(l[11:13]) > 60:ans 1return ans

CSS设置超出范围滚动条和滚动条样式

CSS设置超出范围滚动条和滚动条样式 效果展示 当块级内容区域超出块级元素范围的时候&#xff0c;就会以滚动条的形式展示&#xff0c;你可以滚动里面的内容&#xff0c;里面的内容不会超出块级区域范围。 未设置超出隐藏&#xff0c;显示滚动条 超出隐藏&#xff0c;显示滚动…