继承和多态常见的问题

Posted 小倪同学 -_-

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了继承和多态常见的问题相关的知识,希望对你有一定的参考价值。

文章目录

选择题

  1. 下面哪种面向对象的方法可以让你变得富有( )

A: 继承 B: 封装 C: 多态 D: 抽象

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

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

  1. 面向对象设计中的继承和组合,下面说法错误的是?()

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

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

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

  1. 关于虚函数的描述正确的是( )

A:派生类的虚函数与基类的虚函数具有不同的参数个数和类型 B:内联函数不能是虚函数
C:派生类必须重新定义基类的虚函数 D:虚函数可以是一个static型的函数

  1. 关于虚表说法正确的是( )

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

  1. 假设A类中有虚函数,B继承自A,B重写A中的虚函数,也没有定义任何虚函数,则( )

A:A类对象的前4个字节存储虚表地址,B类对象前4个字节不是虚表地址
B:A类对象和B类对象前4个字节存储的都是虚基表的地址
C:A类对象和B类对象前4个字节存储的虚表地址相同
D:A类和B类虚表中虚函数个数相同,但A类和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

  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. 以下程序输出结果是什么()
#include <iostream>
using namespace std;
class A

public:
	virtual void func(int val = 1)
	
		cout << "A->" << val << endl;
	
	virtual void test()
	
		func();
	
;
class B : public A

public:
	void func(int val = 0)
	
		cout << "B->" << val << endl;
	
;
int main()

	B* p = new B;
	p->test();
	return 0;

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

参考答案:
1.A 2. D 3. C 4. A 5. B
6.D 7. D 8. A 9. C 10. B

问答题

  1. 什么是多态?

多态 是指不同继承关系的类和对象去调用同一函数,产生了不同的行为。多态又分为静态多态和动态多态。

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

重载 是指在同一范围中声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同,也就是说用同一个函数完成不同的功能。
重写(覆盖) 是指两个函数分别在基类和派生类的作用域,这两个函数的函数名、参数、返回值都必须相同(协变例外),且这两个函数都是虚函数。
重定义(隐藏) 是指两个函数分别在基类和派生类的作用域,这两个函数的函数名相同即可。

  1. 多态的实现原理?

构成多态的父类对象和子类对象的成员当中都包含一个虚表指针,这个虚表指针指向一个虚表,虚表当中存储的是该类对应的虚函数地址。因此,当父类指针指向父类对象时,通过父类指针找到虚表指针,然后在虚表当中找到的就是父类当中对应的虚函数;当父类指针指向子类对象时,通过父类指针找到虚表指针,然后在虚表当中找到的就是子类当中对应的虚函数。

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

可以,内联函数是会在调用的地方展开的,是没有地址的,但是内联函数是可以定义成虚函数的,当我们把内联函数定义成虚函数后,编译器就忽略了该函数的内联属性,这个函数就不再是内联函数了,因为虚函数的地址被放到虚表中去了。

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

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

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

不能,因为对象中的虚函数表指针是在构造函数初始化列表阶段才初始化的,如果构造函数是虚函数,那么调用构造函数时对象中的虚表指针都没有初始化。

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

可以,并且最好把基类的析构函数定义成虚函数。当我们new一个父类对象和一个子类对象,并均用父类指针指向它们,在我们使用delete调用析构函数并释放对象空间时,只有当父类的析构函数是虚函数的情况下,才能正确调用父类和子类的析构函数,否则当我们使用父类指针delete对象时,只能调用到父类的析构函数。

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

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

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

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

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

菱形继承子类对象当中有两份父类的成员,会导致数据冗余和二义性的问题。
虚继承对于相同的虚基类在对象当中只会存储一份,若要访问虚基类的成员需要通过虚基表获取到偏移量,进而找到对应的虚基类成员,从而解决了数据冗余和二义性的问题。

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

抽象类体现了虚函数的继承是一种接口继承,强制子类去抽象纯虚函数,如果子类不抽象从父类继承下来的纯虚函数,那么子类也是抽象类也不能实例化出对象。其次,抽象类可以很好的去表示现实世界中没有示例对象对应的抽象类型,比如:植物、人、动物等。

以上是关于继承和多态常见的问题的主要内容,如果未能解决你的问题,请参考以下文章

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

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

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

硬核两万字带你理解C++之多态

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

面向对象:继承多态