C++面向对象------多态
一、虚函数、覆盖、多态
虚函数:在定义时添加virtual关键字的成员函数,叫虚函数
覆盖:在子类中实现与父类中虚函数相同的函数,那么子类中的成员函数会覆盖父类中的成员函数
不同于隐藏,隐藏是在父子类之间名字相同的标识符,只要不够成覆盖,就是隐藏。子类会隐藏父类中的同名函数
多态:如果子类中的成员函数对父类中的成员函数进行了覆盖,那么当一个指向子类的父类指针或引用了子类的父类引用,在调用此同名函数时会调用子类中的函数,而不是父类中的虚函数,这种语法现象叫多态。
多态的意义在于同一种类发出同一种调用而产生不同的反映
二、覆盖、重载、隐藏的条件
覆盖(重写):
a、不在同一作用域,分别在基类和派生类
b、函数名、参数、返回值相同
c、基类函数必须有virtual关键字
d、访问修饰符可以不同
重载:
a、在同一作用域下
b、函数名相同,参数不同
c、返回值可以不同
隐藏:
a、在不同作用域中
b、函数名相同
c、在基类和派生类中只要不构成覆盖就是隐藏
三、多态的条件
1、派生类必须重写基类的虚函数。
2、通过基类指针或引用调用基类的虚函数(该虚函数派生类必须要重写)
3、当指针或引用已经构成多态时,此时调用成员所传的this指针再调用成员函数时也构成多态
4、在子类的构造函数执行前会先调用父类的构造函数,如果调用被覆盖的虚函数,由于子类还没构造完成,因此只能是调用父类中的虚函数构造函数在进入函数体执行时,类中看得见的资源已经全部构造完成
5、在子类的析构函数执行完成后会再调用父类的析构函数,如果调用被覆盖的虚函数,由于子类已经开始析构完成已经不能算是完整的子类了,因此只能调用父类中的虚函数
四、纯虚函数和抽象类
1、纯虚函数
class A
{
public:
virtual void test(void) = 0;
virtual void test(void) const = 0;
};
a、纯虚函数不需要被实现,如果非要实现也不能在类中,必须要在类外(虚函数)
b、纯虚函数如果想调用必须在子类中覆盖,然后以多态的方式调用
2、抽象类
成员函数中有纯虚函数的叫抽象类,这种类不能创建对象。
如果子类继承了抽象类,则必须把父类中的纯虚函数覆盖了,否则他也变成了抽象类不能被实例化
因此抽象类只能以指针或引用的方式指向子类来调用非纯虚函数
3、纯抽象类
所有的成员函数都是纯虚函数,这种类叫纯抽象类
面向对象的四大特性:抽象、封装、继承、多态
纯抽象类是类封装的过程,同时抽象类也可以当做一个统一的接口
六、虚函数表
1、什么是虚函数表,当一个类中有虚函数时,编译器会为这个函数分配一个专门记录这些的虚函数表,在类中会有一个隐藏的指针成员指向这张表
2、如何证明这张表存在
有虚函数的类会比没有虚函数的类(相同的)多4字节,还会添加补齐和对其
3、一个类只有一张虚函数表,所有对象共享一张虚函数表
4、一般对象的前4字节是指向虚函数表的指针
七、动态类型绑定(多态)
1、当使用父类指针或引用指向子类时,编译器并没有立即生成调用函数的指针,而是生成了一段代码,用于检查指针指向的真正的对象是什么类型
2、在代码真正运行时才通过对象的指针找到指向虚函数的成员指针
3、再通过成员指针访问到虚函数表,再从中找到调用的函数地址
4、使用多态会产生额外的一些代码和调用,因此使用多态会降低代码的执行速度
九、虚析构
1、如果通过父类指针或引用指向子类对象,当使用delete释放对象时,此时只能调用父类的析构函数,如果子类中使用new/malloc申请了内存资源,那么将导致内存泄漏
2、解决方法就是把父类的析构函数设置为虚函数
3、在设计类时如果析构函数什么都需要做,编译器也会生成一个空的析构函数,但这样会让继承它的子类会有安全隐患
4、最好把所有的析构函数都设置为虚函数