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

202 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第15天,点击查看活动详情

注意在做类似概念选择题时,有些描述的不是那么准确,此时就去选相对最正确或最错误的。其次我们不可能把所有的概念都直接讲解,比如下面的 “ 内联函数不能是虚函数 ” 和 “ 虚函数可以是一个 static 型的函数 ”,虽然我们没有直接讲解两种概念之间的关系,但是它们的概念我们都学习过,所以这种类型的选项,就需要结合你对不同部分的知识的理解来灵活的判断。

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

    A. 继承

    B. 封装

    C. 多态

    D. 抽象

    📝 A,继承机制是面向对象程序设计使代码可以复用的最重要手段,继承是类设计层次的复用。

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

    A. 继承

    B. 模板

    C. 对象的自身引用

    D. 动态绑定

    📝 D,动态绑定又称后期绑定或晚绑定,就是多态。

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

    答:VS2017 下并没有报错,但要知道 inline 对编译器而言只是建议,实际编译器就必须忽略 inline 属性,因为 inline 是没有地址的,它直接在调用的地方展开,而虚函数要把函数地址放到虚表中去。

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

    答:不能,因为静态成员函数没有 this 指针,而虚函数的调用需要 this 指针,这就不合理了。并且语法也不支持 —— 仅非静态成员函数可以是虚拟的。

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

    答:不能,因为虚函数是为了实现多态,构造函数的调用并没有,也没必要有多态的场景。其次对象的虚表是在编译时生成的,而虚函数表指针是在构造函数初始化列表阶段才初始化的,如果构造函数是虚函数,那么调用构造函数时对象中虚表指针都没有初始化。并且语法也不支持。

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

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

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

#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,要去调用父类的构造函数,谁先继承谁就先调,按理说先由 D 调用 B 的构造函数,再由 B 调用 A 的构造函数,再由 D 调用 C 的构造函数,再由 C 调用 A 的构造函数 (A ➡ B ➡ A ➡ C ➡ D)。但是因为 virtual 后,编译器做了处理,不可能让 B 对 A 初始化一次,C 对 A 再初始化一次,所以应该是 (A ➡ B ➡ C ➡ D)。