C++——继承和多态常见的面试问题

news2024/11/29 8:37:50

目录

  • 1. 继承和多态常见的面试问题
    • 1.1 概念查考
    • 1.2 问答题
    • 1.什么是多态?
    • 2.什么是重载、重写(覆盖)、重定义(隐藏)?
    • 3.多态的实现原理?
    • 4.inline函数可以是虚函数吗?
    • 5.静态成员可以是虚函数吗?
    • 6.构造函数可以是虚函数吗?
    • 7.析构函数可以是虚函数吗?什么场景下析构函数是虚函数?
    • 8.对象访问普通函数快还是虚函数更快?
    • 9.虚函数表是在什么阶段生成的,存在哪的?
    • 10.C++菱形继承的问题?虚继承的原理?
    • 11.什么是抽象类?抽象类的作用?
    • 附加题:如何实现一个不能被继承的类?

对于继承和多态有不清楚的老铁可以看下面两篇文章
继承博客
多态博客

1. 继承和多态常见的面试问题

1.1 概念查考

  1. 下面哪种面向对象的方法可以让你变得富有( A )
    A: 继承
    B: 封装
    C: 多态
    D: 抽象

  2. ( D )是面向对象程序设计语言中的一种机制。这种机制实现了方法的定义与具体的对象无关,而对方法的调用则可以关联于具体的对象。
    A: 继承
    B: 模板
    C: 对象的自身引用
    D: 动态绑定
    分析:动态绑定也叫多态

  3. 面向对象设计中的继承和组合,下面说法错误的是?(C)
    A:继承允许我们覆盖重写父类的实现细节,父类的实现对于子类是可见的,是一种静态复用,也称为白盒复用
    B:组合的对象不需要关心各自的实现细节,之间的关系是在运行时候才确定的,是一种动态复用,也称为黑盒复用
    C:优先使用继承,而不是组合,是面向对象设计的第二原则
    D:继承可以使子类能自动继承父类的接口,但在设计模式中认为这是一种破坏了父类的封装性的表现
    分析:优先使用组合,详情参考继承博客中对于继承的总结与反思

  4. 以下关于纯虚函数的说法,正确的是( A )
    A:声明纯虚函数的类不能实例化对象
    B:声明纯虚函数的类是虚基类
    C:子类必须实现基类的纯虚函数
    D:纯虚函数必须是空函数
    分析:
    A.正确
    B.有纯虚函数的类叫抽象类,它不能用来定义对象,一般用于接口的定义
    C.子类不必实现父类所有的纯虚函数
    D.纯虚函数可以有函数体,只是意义不大

  5. 关于虚函数的描述正确的是( B )
    A:派生类的虚函数与基类的虚函数具有不同的参数个数和类型
    B:内联函数不能是虚函数
    C:派生类必须重新定义基类的虚函数
    D:虚函数可以是一个static型的函数
    分析:
    A.既然是继承下来的那么参数个数和类型以及返回值都是一样的。
    B.正确
    C.可以不重新定义
    D.静态成员函数没有隐藏的this指针,可以通过类名::成员函数名 直接调用,此时没有this无法拿到虚表,就无法实现多态,因此不能设置为虚函数

  6. 关于虚表说法正确的是( D )
    A:一个类只能有一张虚表
    B:基类中有虚函数,如果子类中没有重写基类的虚函数,此时子类与基类共用同一张虚表
    C:虚表是在运行期间动态生成的
    D:一个类的不同对象共享该类的虚表
    分析:
    A.一个类如果是多继承会有多张虚表,
    B.子类是子类的虚表、基类是基类的虚表、即使虚表内虚函数相同,都是基类的虚函数,但是是两份虚表
    C.虚函数表是在编译阶段就生成的,在构造函数初始化列表中进行初始化(虚表指针)。
    D.正确

  7. 假设A类中有虚函数,B继承自A,B重写A中的虚函数,也没有定义任何虚函数,则( D )
    A:A类对象的前4个字节存储虚表地址,B类对象前4个字节不是虚表地址
    B:A类对象和B类对象前4个字节存储的都是虚基表的地址
    C:A类对象和B类对象前4个字节存储的虚表地址相同
    D:A类和B类虚表中虚函数个数相同,但A类和B类使用的不是同一张虚表
    分析:
    对于A、B、C选项:A类和B类前四个字节都是虚表地址,但地址不同。

  8. 下面程序输出结果是什么? (A)

#include<iostream>
using namespace std;
class A {
public:
	A(const char* s) { cout << s << endl; }
	~A() {}
};

class C :virtual public A
{
public:
	C(const char* s1, const char* s2) :A(s1) { cout << s2 << endl; }
};


class B :virtual public A
{
public:
	B(const char* s1, const char* s2) :A(s1) { cout << s2 << endl; }
};


class D :public B, public C
{
public:
	D(const char* s1, const char* s2,const char* s3, const char* s4) :  B(s1, s2), C(s1, s3), A(s1)
	{
		cout << s4 << endl;
	}
};

int main() {
	D* p = new D("class A", "class B", "class C", "class D");
	delete p;
	return 0;
}

A:class A class B class C class D
B:class D class B class C class A
C:class D class C class B class A
D:class A class C class B class D
分析:
B和C都是虚继承,D继承B和C那么D也是虚继承,整个继承体系就一份A
针对D对象,对于A的初始化不在B也不在C中,所以初始化列表的B、C、A中,首先应该初始化的就是A。
然后由于class D :public B, public C那么D继承的顺序就是初始化列表的初始化顺序,
所以接下来是B,然后是C,最后打印D类中的cout << s4 << endl;初始化就结束了。

  1. 多继承中指针偏移问题?下面说法正确的是( C )
class Base1 { public: int _b1; };
class Base2 { public: int _b2; };
class Derive : public Base1, public Base2 { public: int _d; };
int main() {
	Derive d;
	Base1* p1 = &d;
	Base2* p2 = &d;
	Derive* p3 = &d;
	return 0;
}

A:p1 == p2 == p3
B:p1 < p2 < p3
C:p1 == p3 != p2
D:p1 != p2 != p3

分析:p1和p2虽然都是其父类,但在子类内存模型中,其位置不同,所以p1和p2所指子类的位置也不相同,因此p1!=p2,
由于p1对象是第一个被继承的父类类型,所以其地址与子类对象的地址p3所指位置都为子类对象的起始位置,因此p1==p3,所以C正确

  1. 以下程序输出结果是什么(B)
class A
{
public:
	virtual void func(int val = 1) { std::cout << "A->" << val << std::endl; }
	virtual void test() { func(); }
};
class B : public A
{
public:
	void func(int val = 0) { std::cout << "B->" << val << std::endl; }
};
int main(int argc, char* argv[])
{
	B* p = new B;
	p->test();
	return 0;
}

A: A->0
B: B->1
C: A->1
D: B->0
E: 编译出错
F: 以上都不正确

分析:首先父子类中的func符合重写(返回值、函数名、参数列表(只看类型,不用管缺省值)相同),又都是虚函数,
然后子类B继承了父类A的test()函数,当p->test()时,通过test()调用func函数,但是这个test()是从父类继承过来的,里面的this指针是A*,此时this->func(),正好就是父类的指针或者引用去调用,所以构成多态调用。多态调用就跟指针/引用指向的对象有关了,整个过程在B类中,所以此时指向的对象是B,调用B的func函数。
然后接口继承(参数、缺省值、函数名…有关接口的都一样)完全继承于A,多态调用只改变的是实现,所以打印出来的结果是B->1

1.2 问答题

1.什么是多态?

  • 静态绑定又称为前期绑定(早绑定),在程序编译期间确定了程序的行为,也称为静态多态,比如:函数重载
  • 动态绑定又称后期绑定(晚绑定),在程序运行期间,根据具体拿到的类型确定程序的具体行为,调用具体的函数,也称为动态多态。

2.什么是重载、重写(覆盖)、重定义(隐藏)?

答:在这里插入图片描述

3.多态的实现原理?

答:要达到多态,有两个条件,一个是虚函数覆盖,一个是对象的指针或引用调用虚函数。满足多态以后的函数调用,不是在编译时确定的,是运行起来以后到对象中的虚表指针,通过虚表找到要调用虚函数的实现,从而完成多态的正确调用。
具体可以参考该博客多态博客链接

4.inline函数可以是虚函数吗?

答:可以,不过编译器就忽略inline属性,这个函数就不再是inline,因为虚函数要放到虚表中去。inline是建议性关键字,多态调用就没有inline属性了。普通调用继续可以保持inline属性。

5.静态成员可以是虚函数吗?

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

6.构造函数可以是虚函数吗?

答:不能,因为对象中的虚函数表指针是在构造函数初始化列表阶段才初始化的。
把构造函数都变为虚函数了,谁来初始化虚表指针呢?而且这样做也没有意义。

7.析构函数可以是虚函数吗?什么场景下析构函数是虚函数?

答:可以,并且最好把基类的析构函数定义成虚函数。

  • 如果基类没有把析构函数变为虚函数,子类也没有设置,它俩只是继承关系,那么下面的例子中delete p1;delete p2;只是普通调用
    普通调用的话delete对于自定义类型:
    第一步:在空间上执行析构函数,完成对象中资源的清理工作
    第二步:调用operator delete函数释放对象的空间
    那么就会看到下面错误析构的场景
    在这里插入图片描述
    delete看到谁的指针就只析构谁,那么这样的话p2所维护的Student那一部分没有被析构,就造成了内存泄漏。
  • 当基类加上virtual的时候此时Person和Student就具有了多态的特性,这时使用delete的时候就是多态调用,那么多态调用的话,调用析构函数就跟指向的对象有关了,此时Person指针p2指向的对象是student,析构的时候会先析构派生类再析构基类。如下图所示:

在这里插入图片描述

class Person {
public:
	virtual ~Person() { cout << "~Person()" << endl; }
};
class Student : public Person {
public:
	virtual ~Student() { cout << "~Student()" << endl; }
};

int main()
{
	Person* p1 = new Person;
	Person* p2 = new Student;
	delete p1;
	delete p2;
	return 0;
}

所以实现父类的时候,可以无脑给析构函数加virtaul

8.对象访问普通函数快还是虚函数更快?

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

9.虚函数表是在什么阶段生成的,存在哪的?

答:虚函数表是在编译阶段就生成的,一般情况下存在代码段(常量区)的。
在构造函数初始化列表中进行初始化(虚表指针)。

10.C++菱形继承的问题?虚继承的原理?

答:继承博客中有详细介绍哦。注意这里不要把虚函数表和虚基表搞混了。

11.什么是抽象类?抽象类的作用?

答:抽象类强制重写了虚函数,抽象类体现出了接口继承关系。

附加题:如何实现一个不能被继承的类?

1、把基类的构造函数设置为 私有(C++98)
有人会问,把析构设置为私有也行吗?在vs2019下,B* ptr = new B;其实析构设置为私有也是会报错的,但是在vs2013下B* ptr = new B;只是警告,而把构造设置为私有是都会报错的,所以想实现一个不能被继承的类,最好把基类的构造函数设置为私有。
2、类定义时 加final (C++11)

//class A final
class A final
{
//private:
	A()
	{}
};

class B : public A
{};

int main()
{
	B bb;
	//B* ptr = new B;

	return 0;
}

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

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

相关文章

国产低功耗Soc蓝牙语音遥控器芯片HS6621 指纹锁、体脂称等应用方案

随着物联网技术不断发展&#xff0c;家用电器往智能化方向持续迭代&#xff0c;使用红外遥控器这种传统的互动方式已经满足不了实际的使用需求&#xff0c;蓝牙语音遥控器作为人机交互新载体&#xff0c;逐渐取代传统红外遥控器成为家居设备的标配。 相比于传统红外遥控器&…

C语言(数组和指针存储字符串)

目录 一.数组和指针 1.字符串数组和字符数组区别 2.数组存储字符串常量 3.指针存储字符串 二.数组和指针的区别 1.常量和变量 2.修改成本 3.存储成本 一.数组和指针 1.字符串数组和字符数组区别 char ch[]"hello world"&#xff0c;末尾存储着\0&#xff0c;就是字符…

RabbitMQ工作模式

目录1.Work queues工作队列模式1.1 模式说明1.2 代码1.3 测试1.4 小结2.订阅模式类型3.Publish/Subscribe发布与订阅模式3.1 模式说明3.2 代码3.3 测试3.4 小结4.Routing路由模式4.1 模式说明4.2 代码4.3 测试4.4 小结5.Topics通配符模式5.1 模式说明5.2 代码5.3 测试5.4 小结6…

使用crontab执行定时任务

本来这个东西是挺简单的&#xff0c;是我脑子一直没转过来弯&#xff0c;我就想看看有多少人跟我一样&#x1f60f; crontab语法自己去菜鸟教程看看就知道了&#xff0c;没什么难度 需求&#xff1a;每分钟定时执行一个PHP文件或者一个PHP命令 这是需要执行的文件&#xff0…

《Redis实战篇》七、Redis消息队列

7.1 Redis消息队列-认识消息队列 什么是消息队列&#xff1a;字面意思就是存放消息的队列。最简单的消息队列模型包括3个角色&#xff1a; 消息队列&#xff1a;存储和管理消息&#xff0c;也被称为消息代理&#xff08;Message Broker&#xff09;生产者&#xff1a;发送消息…

JAVA锁相关的概念和分类

JAVA对象和对象头 java中object对象一般由对象头、示例数据、填充字节三部分组成其中填充字节是为了补全对象大小为8bit的倍数而存在&#xff0c;没太多功能性要求对象头包括mark word和class point class point存放的是指向堆中数据类型的指针mark word是存储了许多和当前对象…

Linux配置和使用Git

本文已收录至《Linux知识与编程》专栏&#xff01; 作者&#xff1a;ARMCSKGT 演示环境&#xff1a;CentOS 7 ​ 目录 前言 正文 注册Giett构建仓库 注册giett 构建仓库 Linux配置Git 下载Git 配置Git用户名 配置Git账户邮箱 验证是否初始化成功 生成授权证书 获…

重新认识 React Hooks useContext

通常来说,React 数据的传递方式都是一层一层把资料 props 传到子层的 就算第二层(Function Component)、第三层(Button Group Compontn) 根本没有用到这个资料,但是为了传到最底层(button) ,每一层还是必须要传props // App.js const App = () => {const [dark, setDark…

vue3:加载本地图片等静态资源

背景 在我们用 vue2 webpack 的时候&#xff0c;加载图片资源是这样用的&#xff1a; <img :src"require(/assets/test.png)" />这样打包后就会触发 file-loader 打包图片资源&#xff0c;在 dist 文件夹中就可以看到这个图片&#xff08;如果图片较小会打包…

Vue3+vite4使用mockjs进行模拟开发遇到的坑

Vue3vite4使用mockjs进行模拟开发遇到的坑 最近没那么忙了&#xff0c;就想着自己写一个后台管理系统的小demo。刚好最近把Vue3的文档撸了一遍&#xff0c;正好可以顺便练习一下Vue3ts。 插件 1、mockjs是必不可少的。 2、vite-plugin-mock。由于现在创建Vue3项目默认都使用vit…

Django框架之配置信息

静态文件 项目中的CSS、图片、js都是静态文件。一般会将静态文件放到一个单独的目录中&#xff0c;以方便管理。在html页面中调用时&#xff0c;也需要指定静态文件的路径&#xff0c;Django中提供了一种解析的方式配置静态文件路径。静态文件可以放在项目根目录下&#xff0c…

2023年,pmp项目管理师证书有用么?难考吗?

一、这个证书含金量高么&#xff1f; 是高的&#xff0c;在项目管理或者管理领域中&#xff0c;知名度和含金量都是在前列的。看项目岗位要求&#xff1a;“PMP证书优先”&#xff0c;“”具备项目管理资质&#xff08;PMP&#xff09;优先“。 且在行业内几乎是共识了&#…

2023最新软件测试八股文,能不能拿Offer就看你背得怎样了

前言鉴于目前测试就业越来越严峻&#xff0c;内卷也成了测试领域的代名词了。我的一个HR朋友告诉我&#xff0c;由于门槛较低&#xff0c;现在普通测试岗&#xff08;偏功能&#xff09;的投递比已经将近100&#xff0c;也就是一个岗位差不多有百分简历投进来。 所以现在还想从…

【机器学习】lightGBM是什么?

梯度提升法(Gradient Boosting Machine&#xff0c;简记 GBM)以非参数方法&#xff08;不假设函数形式&#xff09;估计基函数&#xff0c;并在“函数空间”使用“梯度下降”进行近似求解。非参数方法包括K近邻法、决策树、以及基于决策树的装袋法、随机森林与提升法等。 01 梯…

MySQL-Innodb引擎事务原理

文章目录1.事务介绍2 事务特性3. 事务的实现原理4 redo log 保证持久性5 undo log 保证原子性6 MVCC 概念6.1 隐藏字段6.2 版本链6.3 ReadView6.3.1readview 版本控制规则7 隔离性 实现7.2 隔离性- REPEATABLE READ 可重复读下8 一致性1.事务介绍 事务是一组操作的集合&#xf…

Vue (4)

文章目录1. 绑定样式1.1 绑定 class 样式1.2 绑定 style 样式2. 条件渲染2.1 v-show2.2 v-if3. 列表渲染3.1 v-for3.2 key 的作用与原理3.3 列表过滤3.4 列表排序1. 绑定样式 说 绑定样式 前&#xff0c;先准备好 以下几个 样式 : <style>.basic {width: 400px;height: 1…

四数之和(详细题解:双指针+排序)

18. 四数之和 难度中等1502 给你一个由 n 个整数组成的数组 nums &#xff0c;和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] &#xff08;若两个四元组元素一一对应&#xff0c;则认为两个四元组重复&#x…

Java 魔法类 Unsafe 详解

阅读过 JUC 源码的同学&#xff0c;一定会发现很多并发工具类都调用了一个叫做 Unsafe的类。 那这个类主要是用来干什么的呢&#xff1f;有什么使用场景呢&#xff1f;这篇文章就带你搞清楚&#xff01; Unsafe 介绍 Unsafe 是位于 sun.misc 包下的一个类&#xff0c;主要提…

要发计算机SCI论文,参考文献应该怎么引用? - 易智编译EaseEditing

SCI论文发表中经常被引用的参考文献主要有以下四种。 一是关于具体的实验的方法&#xff0c;二是支持性或者有冲突的证据&#xff0c;三是比较有用的类似的文献&#xff0c;四是有历史背景的和有意义的文献。 其实归根到底&#xff0c;你引用的SCI的参考文献必须对你的论文发表…

031_SSS_Imagic Text-Based Real Image Editing with Diffusion Models

Imagic: Text-Based Real Image Editing with Diffusion Models 1. Introduction 本文提出了一种新的基于Diffusion的方法称作Imagic&#xff0c;可以实现复杂的基于文本的图像编辑。与之前的方法不同&#xff0c;本文的方法只需要一张输入图像和一个目标文本&#xff0c;并且…