目录
1.静态的多态与动态的多态
2.虚函数定义
3.虚函数的重写
4.多态的实现
传父类的指针或者引用
5.虚函数重写的例外
协变
析构函数的重写
virtual去掉同样完成重写。说明在普通场景下,析构函数是不是虚函数没有影响。若定义一个既可指向父类对象又指向子类对象的指针,不构成重写时,存在内存泄漏的问题
加上virtual构成虚函数后完成析构
子类不写virtual关键字
针对虚函数重写,尽量使用协变,严格按照重写要求来,这样代码更容易维护
6.C++11 override 和 fifinal
7.重载、覆盖(重写)、隐藏(重定义)的对比
不构成多态,就是按指针类型去调用的。构成多态,就是去对象虚表中去找的。
重载
函数重载是指在一个类中声明了多个名称相同但参数列表不同的函数,这些参数可能个数、顺序、类型不同,不能靠返回值类型来判断。
特征:
- 在同一个作用域中;
- 函数的名字相同;
- 参数不同;
- 返回值可以不同;
- 函数重载与virtual修饰无关;
函数重写(也称覆盖)
函数重载是指子类重新定义基类的虚函数。
特征:
- 不在同一作用域(分别位于基类和派生类);
- 函数名字相同;
- 参数相同
- 返回值相同,否则会出错;
- 基类和派生类必须是虚函数,派生类virtual可不加,派生类的访问权限可以和基类的访问权限不一样,基类中的虚函数的访问权限必须是public;
- 重写函数的访问修饰符可以不同;
重定义(又称隐藏)
特征:
- 不在同一个作用域(分别位于基类和派生类中);
- 函数名字相同;
- 返回值可以不同;
- 参数不同
- 参数不同;此时,不论有无 virtual 关键字,基类的函数将被隐藏(注意别与重载以及重写混淆);
- 参数相同;但是基类函数没有 virtual关键字,此时,基类的函数被隐藏(注意别与重写混淆);
- 两个基类和派生类的同名函数不构成重写、就是重定义;
8.抽象类
纯虚函数不只可以声明,还可以定义,但是没意义(即使定义实现出来也无从调用,体现了接口继承)
9.虚函数表
// 1.我们增加一个派生类Derive去继承Base
// 2.Derive中重写Func1
// 3.Base再增加一个虚函数Func2和一个普通函数Func3
class Base
{
public:
virtual void Func1()
{
cout << "Base::Func1()" << endl;
}
virtual void Func2()
{
cout << "Base::Func2()" << endl;
}
void Func3()
{
cout << "Base::Func3()" << endl;
}
private:
int _b = 1;
};
class Derive : public Base
{
public:
virtual void Func1()
{
cout << "Derive::Func1()" << endl;
}
private:
int _d = 2;
};
int main()
{
Base b;
Derive d;
return 0;
}
以笔试题为例子
该对象除了包含int,char,还有一个指针,该指针用来实现多态
从反汇编的角度看虚表
10.几个关于虚表的问题
11.继承关系的虚函数表
虚表存储的区域
单继承中的虚函数表
写一个函数打印虚表,确认虚表中调用的函数
多继承中的虚函数表