0.前言
在后端面试中语言特性的掌握直接决定面试成败,因此本公众号在在后续会持续输出编程语言的必知必会知识点系列。
C++语言一直在增加很多新特性来提高使用者的便利性,但是每种特性都有复杂的背后实现,充分理解实现原理和设计原因,才能更好地掌握这种新特性。
只要出发总会达到,只有出发才会到达,焦虑没用,学就完了,今天一起来学习C++的虚函数考点吧。
通过本文你将了解的以下内容:
C++多态机制
虚函数的基本使用
虚函数的底层实现
纯虚函数和抽象类
虚析构函数
虚函数的优缺点
重要学习资料以及学习解答指导,请点击这里——立即领取**
1.C++多态机制
多态机制简介
C++面向对象的三大特征:
多态(Polymorphism)
封装(Encapsulation)
继承(Inheritance)
从字面上理解多态就是多种形态,具体如何多种形态,多态和继承的关系非常密切,试想下面的场景:
派生类继承使用基类提供的方法,不需更改
同一个方法在基类和派生类的行为是不同的,具体行为取决于调用对象。
后者就是C++的多态需求场景,即同一方法的行为随调用者上下文而异,举个现实生活中类似的栗子,来加深理解:
基类Woker包括三个方法:打卡、午休、干活。
派生类包括产品经理PMer、研发工程师RDer、测试工程师Tester,派生类从基类Worker中继承了打卡、午休、干活三个方法。
打卡和午休对三个派生类来说是一样的,因此可以直接调用基类的方法即可。
但是每个派生类中干活这个方法具体的实现并不一样:产品经理提需求、研发写代码、测试找Bug。
SomeWhere
重要学习资料以及学习解答指导,请点击这里——立即领取**
计算机程序的出现就是为了解决现实中的问题,从上面的例子可以看到,这种同一方法的行为随调用者而异的需求很普遍,然而多态的设计原因只有C++之父Bjarne Stroustrup大佬最清楚了。
静态绑定和动态绑定
要充分理解多态,就要先说什么是绑定?
绑定体现了函数调用和函数本身代码的关联,也就是产生调用时如何找到提供调用的方法入口,这里又引申出两个概念:
静态绑定:程序编译过程中把函数调用与执行调用所需的代码相关联,这种绑定发生在编译期,程序未运行就已确定,也称为前期绑定。
动态绑定:执行期间判断所引用对象的实际类型来确定调用其相应的方法,这种发生于运行期,程序运行时才确定响应调用的方法,也称为后期绑定。
静态多态和动态多态
在C++泛型编程中可以基于模板template和重载override两种形式来实现静态多态。
动态多态主要依赖于虚函数机制来实现,不同的编译器对虚函数机制的实现也有一些差异,本文主要介绍Linux环境下gcc/g++编译器的实现方法。
多态本质上是一种泛型技术,说白了就是试图使用不变的代码来实现可变的算法,要么试图在编译时决定,要么试图在运行时决定。
虚函数与三大特征
虚函数为多态提供了基础,并且借助于继承来发挥多态的优势,从而完善了语言设计的封装,可见虚函数与C++三大特征之间有紧密的联系,是非常重要的特性。
2.虚函数的基本使用
虚函数使用实例
使用virtual关键字即可将成员函数标记为虚函数,派生类继承基类的虚函数之后,可以重写该成员函数,派生类中是否增加virtual关键字均可,代码举例:
#include<iostream>
using namespace std;
class Worker{
public:
virtual ~Worker(){
}
virtual void DoMyWork(){
cout<<"BaseWorker:I am base worker"<<endl;
}
};
class PMer:public Worker{
public:
//virtual void DoMyWork(){
void DoMyWork(){
cout<<"ChildPMer:Tell rd demands"<<endl;
}
};
class RDer:public Worker{
public:
//virtual void DoMyWork(){
void DoMyWork(){
cout<<"ChildRDer:Write code and solve bugs"<<endl;
}
};
class Tester:public Worker{
public:
//virtual void DoMyWork(){
void DoMyWork(){
cout<<"ChildTester:Find bugs and inform rd"<<endl;
}
};
int main(){
//使用基类指针访问派生类
Worker *ptr_pm = new PMer();
Worker *ptr_rd = new RDer();
Worker *ptr_ts = new Tester();
cout<<"#### use ptr #####"<<endl;
ptr_pm->DoMyWork();
ptr_rd->DoMyWork();
ptr_ts->DoMyWork();
ptr_pm->Worker::DoMyWork();
cout<<"-----------------------------"<<endl;
//使用基类引用访问派生类
PMer pmins;
RDer rdins;
Tester tsins