多态概念:
虚函数:
虚函数是一种在基类定义为virtual的函数,并在一个或多个派生类中再定义的函数。虚函数的特点是,只要定义一个基类的指针,就可以指向派生类的对象
虚函数重写---当在子类中定义了一个与父类相同的虚函数时,则称子类的这个函数重写了父类的这个虚函数
虚函数虚在“推迟联编”或者“动态联编”上,一个类函数的调用并不是在编译时刻被确定的,而是在运行时被确定的,由于编写代码的时候并不能确定被调用的是基类的函数还是派生类的函数,所以被称为虚函数
多态:
C++中的虚函数主要作用就是实现多态,简单说父类的指针/引用调用重写的虚函数,当父类指针/引用指向父类对象时调用的时父类的虚函数,指向子类对象调用的是子类的虚函数
多态的构成条件:
基类中必须包含虚函数,并且派生类一定要对基类中的虚函数进行重写
通过基类对象的指针或者引用调用虚函数
普通调用跟对象类型有关,多态调用跟具体对象有关
总结:
派生类重写基类的虚函数实现多态,要求函数名、参数列表、返回值完全相同 (协变除外)
基类中定义了虚函数,在派生类中该函数始终保持虚函数的特性
只有类的成员函数才能定义为虚函数
静态成员函数不能定义为虚函数
构造函数不能为虚函数,虽然可以将 operator= 定义为虚函数,但是最好不要将 operator= 定义为虚函数,因为使用时容易引起混淆
不要在构造函数和析构函数里面调用虚函数,在构造函数和析构函数中,对象是不完整的,可能会发生未定义的行为
最好把基类的析构函数声明为虚函数, 因为析构函数比较特殊,派生类的析构函数跟基类的析构函数名称不一样,但是构成覆盖,这里是因为编译器做了特殊处理
重载、覆盖(重写)、隐藏(重定义)的区别
重载:
在同一作用域内
函数名相同,参数不同
返回值可以不同
重写:
不在同一作用域内,分别在基类和派生类
函数名相同,参数相同,返回值相同(协变除外)
基类函数必须有virtual关键字
访问修饰符可以不同
重定义:
在不同的作用域中(分别在基类和派生类)
函数名相同
在基类和派生类中只要不构成重写就是重定义
抽象类:
在成员函数(必须为虚函数)的形参列表后面加上 =0,则成员函数为纯虚函数。包含纯虚函数的类叫抽象类(接口类),抽象类不能实例化出对象。纯虚函数在派生类中重新定义以后,派生类才能实例化出对象
哪些函数不能定义为虚函数:
不能被继承的函数
不能被重写的函数
普通函数、友元函数、构造函数、内联成员函数、静态成员函数
多态的原理:
虚函数表:通过一块连续内存来存储虚函数的地址,这张表解决了继承、虚函数(重写)的问题。在有虚函数的对象实例中都存在一张虚函数表,虚函数表就像一张地图,指明了实际应该调用的虚函数。
静态绑定与动态绑定:
对象的静态类型:就是它在程序中被声明时所采用的类型,(可理解为类型指针或引用的字面类型)在编译期确定
对象的动态类型:是指“目前所指对象的类型”,(可理解为类型指针或引用的实际类型)在运行期确定
静态绑定:又名前期绑定,绑定的是静态类型,所对应的函数或属性依赖于对象的静态类型,发生在编译期,也称为静态多态
动态绑定:又叫后期绑定,绑定的是动态类型,所对应的函数或属性依赖于对象的动态类型,发生在运行期,也称为动态多态
通常,虚函数是动态绑定,非虚函数是静态绑定,缺省参数值也是静态绑定