继承与多态常见的面试题

目录

1.选择题

2. 问答题 


1.选择题

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

更加富有,当然是继承,可以继承父类的财产 


2. ( D)是面向对象程序设计语言中的一种机制。这种机制实现了方法的定义与具体的对象无关,
而对方法的调用则可以关联于具体的对象。

A: 继承 B: 模板 C: 对象的自身引用 D: 动态绑定

派生类无需理会基类方法的实现,但可以调用继承下来的有访问权限的方法


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

C:优先使用组合,更符合设计模式中的低耦合,高内聚


4. 以下关于纯虚函数的说法,正确的是( A)

A:声明纯虚函数的类不能实例化对象
C:子类必须实现基类的纯虚函数
B:声明纯虚函数的类是虚基类
D:纯虚函数必须是空函数

 声明纯虚函数的类叫抽象类;如果子类未对纯虚函数进行重写,子类也是抽象类;抽象类不能实例化对象;我们并不关心纯虚函数的内部实现。

5.关于虚函数的描写,正确的是(B

A:派生类的虚函数与基类的虚函数具有不同的参数个数和类型

B:内联函数不能是虚函数

C:派生类必须重新定义基类的虚函数D:虚函数可以是一个static型的函数

A:错误,无厘头的要求;B:正确 ;C:错误;D:虚函数与静态成员函数的原理互斥,虚函数需要重写,而在整个继承体系中静态成员函数只有一份。 

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

A:继承几个基类就有几张虚表;B:每个类都有属于自己的虚表;C:虚表是在编译期间生成的,运行时通过对象的实际类型从虚表中找到对应的虚函数地址。 


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:X86下,类对象的前四个字节存储的就是虚表指针,在其他环境就不一定了;B:注意看,这里说的是虚基表,虚基表是在为了解决菱形继承的问题,当一个类同时继承了两个有公共基类的类,会出现两份相同基类的拷贝,这时我们使用虚拟继承,编译器会将共同基类分离出去,并为每个有共同基类的类生成一个虚基表,保存共同基类成员变量的地址偏移量C:每个类都有自己的虚表与虚表指针;


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

#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

创建D对象,根据继承顺序,会依次调用B,C的构造函数,由于虚拟继承,因此A类的成员被放在公共地区,只有一份,A的构造函数会先被调用,接下来是B,C,D


9. 多继承中指针偏移问题?下面说法正确的是( 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

本题考察切片赋值,下图是d的内存结构。


10. 以下程序输出结果是什么(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: 以上都不正确
本题是比较绕的,这里有一个指向B对象的p指针,p调用test,test是继承自A的虚函数,调用func,这里由于实际对象是B类型,因此调用B重写后的func。关键点来了:由于重写是重新实现,因此这里的函数声明依旧是A中func的声明,因此最后输出B->1. 

2. 问答题 

1. 什么是多态?

答:通俗来讲就是不同对象对同一行为做出的不同动作。

当一个父类引用指向子类对象时,通过父类引用调用的方法可能会触发子类中重写(覆盖)的方法,从而实现了多态性。多态性可以提高代码的灵活性和可扩展性,使得程序更易于维护和扩展。


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

答:重载是在同一作用域内,同函数名,但函数参数的个数/顺序/类型不同的多个函数。

重定义(隐藏)是在继承体系中,如果不是虚函数,只要有同名的函数就构成隐藏。

重写是多态中的概念,继承体系中,子类继承父类的虚函数,并对其内部实现进行了重新定义,以实现多态性。
3. 多态的实现原理?

答:多态的实现原理其实很简单,含有虚函数的类会有一个虚表指针,指向自己的虚函数表(单继承,多继承的子类会有多个,派生类都是只有一个),所谓虚函数表就是虚函数指针数组,里面存放着虚函数的指针。当使用基类的引用或指针调用虚函数时,编译器会查看调用函数的实际对象类型,然后去这个实际类型的类的虚表中找到对应的虚函数,从而实现多态。


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

答:可以,

  1. 对于非虚函数,inline修饰的函数只是建议编译器将函数体内联展开,具体是否内联取决于编译器的实现。
  2. 对于虚函数,即使使用inline关键字修饰,也不一定会内联展开。因为虚函数的调用通常会通过虚表来实现多态,编译器需要进行动态绑定,这可能会导致无法进行内联优化。

因此,虚函数可以被声明为inline,但在实际情况下,编译器可能不会将其内联展开。通常情况下,建议将虚函数的定义放在头文件中,这样可以避免链接错误,并让编译器更容易进行优化。

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

答:不能,

访问方式冲突:因为静态成员函数没有this指针,静态成员函数的访问必须是类型::函数名,但虚函数的访问必须是基类的引用或指针;

存储方式冲突:静态成员函数在整个继承体系中只有一份,但虚函数在派生类中是可能重写的,因此虚函数会有很多份。

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

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


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

答:可以,并且最好把基类的析构函数定义成虚函数。任何一个类的析构函数在编译完成后都会被处理成destructor函数,也就是说继承体系中只要父类的析构函数是虚函数,那么其子类的析构就全是虚函数。


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

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


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

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


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

答:假设有类A,B,C,D,类B,C,继承于A,类D继承于类A,C菱形继承的问题在于类D中会存在两份类A继承下来的成员变量,造成空间浪费与二义性。虚继承的原理就是在中间类前加virtual,使得类A继承的成员变量单独放置,然后在虚基类的子类(D)中生成一份虚基表(存放访问A成员变量的地址偏移量),并将虚基表指针给B和C保存。

切记,虚基表和虚表不一样。


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

答:有纯虚函数(虚函数后加=0)的类叫抽象类,抽象类不能实例化。抽象类的子类只有在重写了纯虚函数以后才能实例化对象。

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

  • 24
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是关于Python中封装、继承多态面试题: 1. 什么是封装?如何实现? 封装是面向对象编程中的一种重要概念,它指的是将数据和方法包装在类中,对外部世界隐藏类的内部细节,从而提高了代码的安全性和可维护性。在Python中,可以通过将属性设置为私有属性,并提供公有的访问方法来实现封装。 2. 什么是继承?如何实现? 继承是面向对象编程中的一种重要机制,它指的是一个类可以通过继承另一个类的属性和方法来扩展自己。在Python中,可以通过在类定义中指定要继承的父类来实现继承。 3. 什么是多态?如何实现? 多态是面向对象编程中的一种重要特性,它指的是同一个方法可以被不同的对象调用,产生不同的结果。在Python中,可以通过方法重写和方法重载来实现多态。方法重写指的是子类重写父类的方法,从而实现不同的功能;方法重载指的是在同一个类中定义多个同名方法,但参数类型和个数不同,调用时编译器会根据实际参数类型和个数选择调用哪个方法。 4. 在Python中如何实现接口? 在Python中,没有像Java和C#等语言中的接口概念。但是可以通过抽象基类(Abstract Base Class,简称ABC)来实现类似于接口的功能。ABC是Python中的一种特殊类,它不能被实例化,只能被继承,子类必须实现指定的抽象方法,从而保证了程序的正确性和可维护性。 以上就是关于Python中封装、继承多态面试题,希望能对你有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值