C++ 面试题之继承/重载/多态

继承、重载、多态

1. 什么是继承?继承的作用是什么?

  • 继承
    继承是指在已存在的类的基础上扩展新的类,即一个对象可以直接使用另一个对象的属性和方法。
  • 继承的作用
    借助继承,可以扩展原有的代码,应用到其它程序中去,而不必重新编写这些代码。

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

  • 重载:在C++程序中,可以将语义、功能相似的几个函数用同一个名字表示,但是其参数列表不同(包括类型,个数,顺序不同),即函数重载。
  1. 相同的作用域(在同一个类中)
  2. 函数名相同
  3. 参数不同
  4. virtual 关键字可有可无

  • 覆盖/重写:派生类中存在与基类具有,相同参数列表,相同返回值的同名函数,并且基类的函数要带有 virtual 关键字。协变例外(派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指针或者引
    用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。)
  1. 不同的作用域(分别位于派生类与基类)
  2. 函数名相同
  3. 参数相同
  4. 基类中必须有 virtual 关键字

注意:重写基类函数的时候,会自动转换这个函数为 virtual 函数,不管有没有加 virtual,因此重写的时候不加 virtual 也是可以的,不过为了易读性,还是加上比较好


  • 隐藏(重定义):隐藏,是指派生类的函数屏蔽了与其同名的基类函数,注意只要是同名函数并且基类函数没有 virtual 修饰,不管参数列表是否相同,基类函数都会被隐藏。
  1. 不同的作用域(分别位于派生类与基类)
  2. 如果派生类的函数与基类的函数同名,不管参数列表是否不同并且基类函数没有 virtual 关键字。此时,基类的函数将被隐藏(注意别于重载混淆,重载是在同一个类中,而重写是在不同类中

3. 什么是菱形继承?菱形继承的问题是什么?怎么解决?解决的原理是什么?

  • 什么是菱形继承?
    一个派生类有多个基类,而多个基类又由同一个类派生。
  • 菱形继承的问题是什么?
    会让高层的基类在底层的派生类中拥有多份成员(数据冗余),造成不合理
  • 怎么解决?
    虚继承
    解决的原理是什么?
    如果一个类中有重复的虚基类,就会让多个 vbptr 指向同一个虚基类作用域,这样就避免了数据冗余的问题

4. 什么是is-a,什么是has-a?继承和组合哪个更好?为什么?

is-a(是 “a”,小明是人类)表示属于关系。比如兔子属于一种动物(继承关系)。
has-a(有 “a”,汽车有轮胎)表示组合,包含关系。比如兔子有腿,有耳朵,就不能说兔子腿属于一种兔子(不能说是继承关系)


对两个类之间"is a"或是"has a"关系的分析,有助于我们确定它们之间是否存在继承关系,避免设计上的错误,因而达到提高代码重用性的目的

5. 什么是多态?多态的条件是什么?多态的实现原理是什么?为什么使用多态?(几乎必考,重点重点重点)

  • 什么是多态?
    定义:“一个接口,多种方法”,程序在运行时才决定要调用的函数。通俗的将,就是允许将子类的对象赋给基类的指针或引用。多态又分为两种:静态多态(早绑定)和动态多态(晚绑定)。静态多态是通过函数重载实现的,动态多态是通过虚函数实现的。接口多态指的是“一个接口多种形态”。每一个对象内部都有一个虚表指针,该虚表指针被初始化为本类的虚表。所以在程序中,不管你的对象类型如何转换,但该对象内部的虚表指针是固定的,所以呢,才能实现动态的对象函数调用,这就是C++多态性实现的原理。
  • 多态的条件是什么?
  1. 必须通过基类的指针或引用调用虚函数
  2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写
  • 多态的实现原理是什么?
    父类或者接口的引用变量可以指向子类或者具体类的实例对象,由于程序调用方法是在运行期间才动态绑定的,那么引用变量所指向的具体实例对象在运行期间才确定。所以这个对象的方法是运行期间正在内存运行的这个对象的方法而不是引用变量的类型中定义的方法。
  • 为什么使用多态?
    接口重用。封装可以使代码模块化,继承可以扩展已有的代码,它们的目的都是代码复用。而多态的目的是接口重用。

6. 为什么C++支持重载,C语言不支持?

其实主要是通过两种语言的命名机制决定的。通过汇编代码我们可以知道,对 C 语言中函数名的命名机制是直接使用函数名来命名的,所以在 C 语言中,每一个函数名只能对应一个函数,所以 C 语言不支持重载;而 C++ 的函数命名机制是通过 “函数名+参数列表” 进行命名的,所以它们在汇编中命名不同,可以实现函数重载。

7. inline函数、构造函数、析构函数、static 函数是否可以是虚函数?

  • inline函数不能是虚函数:因为 inline 函数没有地址,无法把地址放到虚函数表中
  • 构造函数不能是虚函数:因为对象的虚函数表指针是在构造函数初始化列表阶段才初始化的
  • 析构函数可以是虚函数:并且最好把基类的析构函数定义成虚函数
  • static 函数不能是虚函数:因为静态成员函数没有 this 指针,使用 类型::成员函数的调用方式无法访问虚函数表,所以静态成员函数无法放进虚函数表

8. 基类为什么需要虚析构函数?

当用基类的指针或者引用指向派生类对象时,只有基类将析构函数定义成虚函数,派生类中的析构函数才能覆盖其基类的析构函数,从而再释放对象时,才能够调用到派生类的析构函数,防止内存泄漏。

9. 什么是虚函数?什么是纯虚函数?虚函数存在哪儿?虚表存在哪?

  • 什么是虚函数?什么是纯虚函数?
    虚函数就是在函数前面加上 virtual 关键字。在虚函数的后面写上=0,则这个函数为纯虚函数(纯虚函数和空函数不同),包含纯虚函数的类叫做抽象类。
  • 虚函数存在哪儿?虚表存在哪?
    虚表的指针不是虚表)存在于对象中,指向虚函数的指针不是虚函数)存在于虚函数表中,虚函数和普通函数一样,都存在于代码段。C++中虚函数表位于只读数据段(.rodata),也就是C++内存模型中的常量区;而虚函数则位于代码段(.text),也就是C++内存模型中的代码区。。

10. 哪些函数不能成为虚函数?

  • 普通函数
    普通函数不属于成员函数,是不能被继承的。普通函数只能被重载,不能被重写,因此声明为虚函数没有意义。因为编译器会在编译时绑定函数。而多态体现在运行时绑定。通常通过基类指针指向子类对象实现多态。
  • 友元函数
    友元函数不属于类的成员函数,不能被继承。对于没有继承特性的函数没有虚函数的说法。
  • 构造函数
    因为对象的虚函数表指针是在构造函数初始化列表阶段才初始化的。
  • 内联函数
    因为 inline 函数没有地址,无法把地址放到虚函数表中。我们需要知道内联函数就是为了在代码中直接展开,减少函数调用花费的代价。也就是说内联函数是在编译时展开的。而虚函数是为了实现多态,是在运行时绑定的。因此显然内联函数和多态的特性相违背。
  • 静态成员函数
    首先静态成员函数理论是可继承的。但是静态成员函数是编译时确定的,无法动态绑定,不支持多态,因此不能被重写,也就不能被声明为虚函数
  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值