继承和多态常见的面试问题(代码片段)

在下赵某人 在下赵某人     2022-11-23     274

关键词:

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

1 概念查考

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

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

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

  • 析:C明显错误。优先使用组合,而不是多态。
  1. 以下关于纯虚函数的说法,正确的是( )
    A:声明纯虚函数的类不能实例化对象 B:声明纯虚函数的类是虚基类
    C:子类必须实现基类的纯虚函数 D:纯虚函数必须是空函数
  • 析:
    B:菱形虚拟继承中,共享的基类对象称为“虚基类”。
    C:子类不实例化对象就无需实现基类的纯虚函数。
    D:纯虚函数是无需实现,而非必须是空函数
  1. 关于虚函数的描述正确的是( )
    A:派生类的虚函数与基类的虚函数具有不同的参数个数和类型 B:内联函数不能是虚函数
    C:派生类必须重新定义基类的虚函数 D:虚函数可以是一个static型的函数
  • 析:
    B:inline函数在调用的地方展开了,故inline函数没有地址。而虚函数需要把函数地址放进虚函数表中。
    D:虚函数的调用关系:this -> vptr -> vtable ->virtual function。而static修饰的函数无this指针。
  1. 关于虚表说法正确的是( )
    A:一个类只能有一张虚表
    B:基类中有虚函数,如果子类中没有重写基类的虚函数,此时子类与基类共用同一张虚表
    C:虚表是在运行期间动态生成的
    D:一个类的不同对象共享该类的虚表
  • 析:
    A:多重继承会有多个虚函数表,几重继承,就会有几个虚函数表。
    C:虚表是在编译期间生成的
  1. 假设A类中有虚函数,B继承自A,B重写A中的虚函数,也没有定义任何虚函数,则( )
    A:A类对象的前4个字节存储虚表地址,B类对象前4个字节不是虚表地址
    B:A类对象和B类对象前4个字节存储的都是虚基表的地址
    C:A类对象和B类对象前4个字节存储的虚表地址相同
    D:A类和B类虚表中虚函数个数相同,但A类和B类使用的不是同一张虚表
  • 析:
    B:存储的都是虚函数表指针,而不是虚基表。虚基表是菱形虚拟继承那部分的概念。虚基表中存了偏移量。
  1. 下面程序输出结果是什么? ()
#include<iostream>
using namespace std;
class A
public:
 A(char *s)  cout<<s<<endl; 
 ~A()
;
class B:virtual public A 
public:
 B(char *s1,char*s2):A(s1)  cout<<s2<<endl; 
;
class C:virtual public A 
public:
 C(char *s1,char*s2):A(s1)  cout<<s2<<endl;
;
class D:public B,public C 
public:
 D(char *s1,char *s2,char *s3,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

  • 析:
    初始化子类前会先调用父类构造初始化父类,多继承会按父类继承的顺序进行构造。因为编辑器的优化,菱形虚拟继承公共的父类A只会调用构造初始化一次。
  1. 多继承中指针偏移问题?下面说法正确的是( )
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

  1. 以下程序输出结果是什么()
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: 以上都不正确

  • 析:
    子类对象调用test,会调用到从父类继承下来的test。test里面会用父类的this指针调用fun函数,且父类的fun完成了虚函数重写。此时构成多态,调用到父类的fun函数。父类继承子类时,普通函数为实现继承,而虚函数继承为接口继承。故用了父类fun的接口,子类fun的实现。故val的缺省值为1,故结果为:B->1。
    调父类的成员函数,父类成员函数里面的this指针是父类指针。成员函数中this指针的类型,始终都是 T* const这种类型的。

参考答案:

  1. A 2. D 3. C 4. A 5. B 6. D 7. D 8. A 9. C 10. B

2 面试题:

  1. 为什么析构函数建议设置为虚函数
    父类的析构函数强力建议设置为虚函数,这样动态释放父类指针所指的子类对象时,能够达到析构的多态

  2. 构造时,先构造父类,再构造子类部分,因此父类和子类的构造函数都会被调用;

  3. 析构时,要分情况:

(1) (父类析构函数不是虚函数)使用父类指针指向子类对象,析构该子类对象时,只会调用父类析构函数,因为不具多态性;代码理解:

class child:public parent;
parent *p = new child();  //父类指针指向子类对象
delete p;  //用父类指针调用,且未构成多态,调用的是父类析构函数,子类析构函数不会被调用

(2)上面存在一个问题,如果子类也分配的资源就会导致无法释放,使用虚函数可以解决上述问题。

class parent
	virtual	~parent();
;
class child:public parent

	~child();
;
parent *p = new child();  //父类指针指向子类对象
delete p;   //用父类指针调用,但构成多态,调用的是子类的析构函数。
            //子类析构函数会先析构子类部分,再调用父类的析构函数析构父类部分

(3)对于第一种非虚函数的情况,如果是用子类指针指向子类对象,那么会先调用子类析构函数,再调用父类析构函数,子类释放子类中分配的,父类分配父类中分配的。

class child:public parent;
child*p = new child();  //子类指针指向子类对象
delete p;   //虽然未构成多态,但用子类指针调用,调用的是子类的析构函数。
            //子类析构函数会先析构子类部分,再调用父类的析构函数析构父类部分

第三种方法也能够让父类和子类的资源都释放掉,使用virtual是为了使父类指针具有通用性
2. 多态调用的条件

  • 在继承中要构成多态还有两个条件:
    1. 必须通过基类的指针或者引用调用虚函数
    2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写

  • 单说只构成多态调用
    1. 只要通过指针或者引用调用,调用的是虚函数即可,无需完成重写。入下例:

  • 首先,p1会使程序奔溃,单p2调用则正常运行。

  • 因为:类的成员函数存在公共代码段,空指针调用成员函数只要未涉及this指针即可正常运行。
    p1是指针调用,调用的是虚函数,所以是多态调用。多态调用要到对象中找虚函数表指针,要用到this指针,故会报错。
    p2是普通调用,无需this指针,且成员函数test中也未用到this指针,故不会报错。

  1. 为何static成员函数不能为virtual

  2. 虚函数依靠vptr和vtable来处理。vptr是一个指向虚函数表vtable的指针。因为vptr是类的成员,故需要用this指针来访问它,而静态成员函数没有this指针。所以static函数不能为virtual。

  3. 虚函数的调用关系:this -> vptr -> vtable ->virtual function

  4. 内联函数可以是虚函数吗?
    答:
    语法上:不可以,因为:inline函数在调用的地方展开了,故inline函数没有地址。而虚函数需要把函数地址放进虚函数表中。
    实际中:可以,不过编译器就忽略inline属性,这个函数就不再是inline,因为虚函数要放到虚表中去。(vs下是这样,其他编译器请自行验证)

  5. 构造函数可以是虚函数吗?
    答:不能,因为:

  6. 具体描述:如果构造函数是虚函数,那么调用构造函数前先要用虚表指针找到虚表,而此时对象中虚表指针都还没有初始化,因为对象中的虚表指针是在构造函数初始化列表阶段才初始化的。

  7. 简而言之:构造的时候才初始化虚表指针,而如果构造函数是虚函数,则构造前就要用到虚表指针,互相矛盾。

  8. 对象访问普通函数快还是虚函数更快?
    答:如果是普通对象,是一样快的,因为不满足多态调用的两个条件,会像普通函数那样调用而不会走多态调用的路子。如果是对象指针或者是对象引用,则调用的普通函数快,因为构成多态,运行时调用虚函数需要到虚函数表中去查找。

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

继承和多态常见的面试问题(代码片段)

继承和多态常见的面试问题1概念查考下面哪种面向对象的方法可以让你变得富有()A:继承B:封装C:多态D:抽象()是面向对象程序设计语言中的一种机制。这种机制实现了方法的定义与具体的对象无关,而对方法的调用则可以关联... 查看详情

java常见面试知识点汇总(代码片段)

...和具体含义?面向对象变成的三个特征:封装、继承与多态。封装:是指将某事物的属性和行为包装到对象中,这个对象只对外公布需要公开的属性和行为;继承:子对象可以继承父对象的属性和行为࿱... 查看详情

java初级面试题(代码片段)

Java面向对象有哪些特征继承、封装、多态三大特征相互相乘封装:封装内部的一些实现机制,就跟方法封装一些实现的机制一样继承:就是从已有的类派生出新的类,新的类继承已有类的行为和属性,并扩展... 查看详情

java面试题(代码片段)

...是把属性私有化封装在一个类里面,只能通过方法去访问继承子类继承父类,从而继承了父类的方法和属性抽象比如一个英雄类,抽象出了name,hp这些属性,使得开发过程中更加易于理解多态多态分操作符的多态和类的多态。类... 查看详情

使用继承和多态来解决一个常见的游戏问题

】使用继承和多态来解决一个常见的游戏问题【英文标题】:Usinginheritanceandpolymorphismtosolveacommongameproblem【发布时间】:2011-02-1515:50:23【问题描述】:我有两节课;让我们称他们为食人魔和巫师。(所有字段都是公开的,以使示... 查看详情

java面试题详解一:面向对象三大特性(代码片段)

一,多态:1.面向对象四大基本特性:抽象,封装,继承,多态抽象,封装,继承是多态的基础。多态是抽象,封装,继承的表现。2.什么是多态不同类的对象对同一消息作出不同的响应叫做多态3.多态的作用简单来说:解藕。详... 查看详情

java经典面试题:一个接口多个实现类,如何指定实现类(代码片段)

...。一说到多态,大家就会立即条件反射的想到:继承、封装。是的,多态可以理解为继承和封装的大成者。先简单的看下什么是继承和封装。(小伙子,听我的,这个东西很重要滴)继承继承就是子类... 查看详情

java面向对象特性与多态面试题(代码片段)

...b72e&scene=21#wechat_redirect 面向对象三大特性:封装、继承、多态    四种特性:封装、继承、多态、抽象封装:把过程和数据包围起来,使得数据的访问只能通过已定义的接口,保证了对象被访问的隐私性和可靠性。---... 查看详情

java经典面试题:一个接口多个实现类,如何指定实现类(代码片段)

...。一说到多态,大家就会立即条件反射的想到:继承、封装。是的,多态可以理解为继承和封装的大成者。先简单的看下什么是继承和封装。(小伙子,听我的,这个东西很重要滴)继承继承就是子类... 查看详情

c++继承多态关系中的赋值运算符的重载=operator()(代码片段)

...符的重载是非常有用也是非常常见的,但是当涉及到继承和多态时情况可能就比较复杂了。对于多态,则一般都会涉及到虚函数重载,而我们在继承体系中如果重载赋值运算符则需要考虑对父类的赋值。假设有如下的... 查看详情

java面试题及答案(基础题122道,代码题19道)(转)(代码片段)

...象包括两个方面,一是过程抽象,二是数据抽象。  2.继承:  继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。对象的一个新类可以从现有的类中派生,这个过程称为类继承。... 查看详情

java初级面试题(代码片段)

Java面向对象有哪些特征继承、封装、多态三大特征相互相乘封装:封装内部的一些实现机制,就跟方法封装一些实现的机制一样继承:就是从已有的类派生出新的类,新的类继承已有类的行为和属性,并扩展... 查看详情

java常见面试知识点汇总(代码片段)

...和具体含义?面向对象变成的三个特征:封装、继承与多态。封装:是指将某事物的属性和行为包装到对象中,这个对象只对外公布需要公开的属性和行为;继承:子对象可以继承父对象的属性和行为࿱... 查看详情

深度剖析—继承和多态(代码片段)

深度剖析—继承和多态继承什么是继承?继承的语法注意事项继承的意义super关键字super和this关键字的区别🔺protected关键字多层继承final关键字继承和组合多态多态概念多态前提:向上转型向下转型动态绑定(运行时绑... 查看详情

java面向对象三大特征之继承和多态(代码片段)

目录前言一、继承1.基本概述2.继承中的成员访问特点3.方法重写4.Java中继承的注意事项继承代码案例二、多态1.多态概述2.多态中成员访问特点3.多态的好处和弊端4.多态中的转型多态代码案例总结前言  上一篇博客我们介绍了Jav... 查看详情

c++多态(代码片段)

...al重载、覆盖(重写)、隐藏(重定义)的对比三、抽象类接口继承和实现继承四、多态的原理虚函数表多态的原理动态绑定与静态绑定五、单继承和多继承关系的虚函数表单继承中的虚函数表多继承中的虚函数表菱形继承、菱形虚拟... 查看详情

面向对象--继承和多态(代码片段)

一、继承继承的类称为子类,被继承的类称为基类,父类,或超类。继承的优点:简化了代码提高了代码的健壮性提高了代码的安全性多态的前提缺点:耦合与内聚。耦合性越低,内聚性越高,代码越好。比如,有一个Animal类,... 查看详情

继承和多态(代码片段)

继承继承的概念继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相... 查看详情