1. 多态的概念

多态是面向对象编程中的一种特性,它允许使用相同的接口来表示不同的实现。(去完成某个行为时,当不同的对象去完成时会产生不同的状态)

2. 多态的定义

在C++中,多态性的实现主要依赖于继承和虚函数。基类中的虚函数可以在派生类中被重写,从而实现了运行时的多态性。这种机制被称为动态绑定。同一接口可以有不同的实现方式,多态允许基类的指针指向子类的方法。

3. 多态的实现

3.1 在继承中构成多态的条件:

  1. 必须通过基类的指针或者引用调用虚函数

  2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写

3.2 虚函数

虚函数:即被virtual的类成员函数称为虚函数

class Person{public:virtual void BuyTicket(){cout << "买票全价" << endl;}};

3.3 虚函数的重写

虚函数的重写(覆盖):派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型函数名字参数列表完全相同),称子类的虚函数重写了基类的虚函数

特殊情况:

  1. 协变(基类与派生类虚函数返回值类型不同)

派生类重写基类虚函数时,与基类虚函数返回值类型不同,即基类虚函数返回基类对象的指针或者引用。派生类虚函数返回派生类对象的指针或者引用时,称为协变。

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; }};
  1. 析构函数的重写(基类与派生类析构函数的名字不同)

如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加 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关键字

  1. final:修饰虚函数,表示该虚函数不能再被重写

  1. 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. 动态绑定与静态绑定

  1. 静态绑定又称为前期绑定(早绑定),在程序编译期间确定了程序的行为,也称为静态多态
  2. 动态绑定又称为后期绑定(晚绑定),是在程序运行期间,根据具体拿到的类型确定程序的具体行为,调用具体的函数,也称为动态多态