1. 多态的概念
多态是面向对象编程中的一种特性,它允许使用相同的接口来表示不同的实现。(去完成某个行为时,当不同的对象去完成时会产生不同的状态)
2. 多态的定义
在C++中,多态性的实现主要依赖于继承和虚函数。基类中的虚函数可以在派生类中被重写,从而实现了运行时的多态性。这种机制被称为动态绑定。同一接口可以有不同的实现方式,多态允许基类的指针指向子类的方法。
3. 多态的实现
3.1 在继承中构成多态的条件:
必须通过基类的指针或者引用调用虚函数
被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写
3.2 虚函数
虚函数:即被virtual
的类成员函数称为虚函数
class Person{public:virtual void BuyTicket(){cout << "买票全价" << endl;}};
3.3 虚函数的重写
虚函数的重写(覆盖):派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型
、函数名字
、参数列表完全相同
),称子类的虚函数重写了基类的虚函数
特殊情况:
- 协变(基类与派生类虚函数返回值类型不同)
派生类重写基类虚函数时,与基类虚函数返回值类型不同,即基类虚函数返回基类对象的指针或者引用。派生类虚函数返回派生类对象的指针或者引用时,称为协变。
class A{};class B:public A{};class Person{public:virtual A* f() { return new A; }};class Student :public Person{public:virtual B* f() { return new B; }};
- 析构函数的重写(基类与派生类析构函数的名字不同)
如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加 virtual
关键字,都与基类的析构函数构成重写,虽然基类与派生类析构函数名字不同。虽然函数名不相同,看起来违背了重写的规则。其实这里可以理解为编译器对析构函数做了特殊处理,编译器后析构函数的名称统一处理成destructor
class Person{public:virtual ~Person() { cout << "~Person()" << endl; }};class Student :public Person{virtual ~Student() { cout << "~Student" << endl; }};int main(){Person* p1 = new Person;Person* p2 = new Student;delete p1;delete p2;}//~Person()//~Student//~Person()
3.4 C++11关键字
- final:修饰虚函数,表示该虚函数不能再被重写
- voerride :检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错
class Car{public: virtual void Drive(){}};class Benz :public Car{public:virtual void Drive() override { cout << "Benz-舒适" << endl; }};
3.5 重载/重写/重定义的对比
4. 抽象类
4.1 概念
在虚函数的后面加上=0,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象,纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。
4.2 接口继承和实现继承
普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的是函数的实现。虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口,目的是为了重写,达到多态,继承的是接口。所以如果不实现多态,不要把函数定义成虚函数
5. 动态绑定与静态绑定
- 静态绑定又称为前期绑定(早绑定),在程序编译期间确定了程序的行为,也称为静态多态
- 动态绑定又称为后期绑定(晚绑定),是在程序运行期间,根据具体拿到的类型确定程序的具体行为,调用具体的函数,也称为动态多态