目录1.类/对象的多态性***运行时多态动态多态怎样达到运行时多态程序是如何在运行时识别并调用子类实现的为什么要实现多态2.全局函数/同类中成员函数的多态性编译时多态静态多态虚函数使用规则override多态的实现原理抽象类和接口类抽象类定义特点接口类定义特点1.类/对象的多态性***是考虑不同层次或同一层次函数的不同状态意思就是同一个接口不同的实现运行时多态动态多态类成员函数在编译时无法确定调用的位置只有在运行时依据函数名和参数表来确定你调用的位置怎样达到运行时多态条件继承重写基类的虚函数应用场景虚函数virtualvoid hi(){...}vfptr 虚函数指针应用场景父类的引用/指针指向子类的对象//继承重写基类的虚函数应用场景 class Person { public: virtual void hi() { cout Person hi() endl; } }; //1.继承 class Student :public Person { //2.重写基类的虚函数 //override 关键字作用 确认此成员函数是否正确重写 //重写虚函数的三同返回类型、函数名、参数表 virtual void hi() override { cout Student hi() endl; } }; ​ int main() { /*Person p1; p1.hi();*/ ​ //3.运行时多态应用场景 Person* p1 new Student(); ​ p1-hi(); return 0; }Student hi()D:\C\x64\Debug\C.exe (进程 6440)已退出代码为 0。 按任意键关闭此窗口. . .class Person { public: virtual void hi() { cout Person hi() endl; } }; ​ class Student :public Person { virtual void hi() override { cout Student hi() endl; } }; ​ class Teacher : public Person { virtual void hi() override { cout Teacher hi() endl; } }; ​ class Worker : public Person { virtual void hi() override { cout Worker hi() endl; } }; ​ void hello(Person person) { // 因为此处是Person类的引用 // 同时, hi成员函数是虚函数因此编译时无法确认调用的位置编译器不知道你这个指针 / 引用最终指向的是哪个子类对象 // 只有在程序运行之后调用此函数传入具体的对象时才能确认调用位置 person.hi(); } ​ int main() { Person p1; Worker w1; Teacher t1; Student s1; ​ //运行时多态体现 hello(p1); hello(w1); hello(t1); hello(s1); ​ return 0; }Person hi() Worker hi() Teacher hi() Student hi()D:\C\x64\Debug\C.exe (进程 14088)已退出代码为 0。 按任意键关闭此窗口. . .程序是如何在运行时识别并调用子类实现的只要类里面有虚函数对象就会自动带一个虚指针父类对象里面有父类虚指针子类对象里面有子类虚指针。只要子类重写了父类的虚函数那么子类的虚表会把父类虚函数的地址 “覆盖掉”。存放的就是子类函数的地址调用会调用子类的为什么要实现多态主要是为了统一接口方便扩展提高代码的可维护性例子比如说如果没有多态要为每个子类准备一个单独的函数。catspeak,dogspeak,pigspeak。有了多态之后一个函数可以解决所有的子类只写一个speak函数里面传子类的对象函数规范简洁。易于扩展主要就是比如说要写一个birdspeak,不用新写一个函数只用创建一个类继承animal重写虚函数。提高代码的可维护性要修改的话改一处就好重复代码太少了2.全局函数/同类中成员函数的多态性编译时多态静态多态类成员函数在编译时可以确定调用的位置实现方式函数重载、运算符重载、模板可以根据参数/类型在编译时确定调用哪个版本虚函数使用规则1.派生类/子类重写虚函数时必须三同返回类型、函数名、参数表2.虚函数只能属于类只修饰类的成员函数外部全局函数是无法使用virtual关键字3.静态成员函数内联函数构造函数和拷贝构造函数不能为虚函数不能加virtual静态成员函数在子类中只能覆盖重定义不能加virtual因为它没有this指针构造函数用于初始化成员创建类时必须唯一确定拷贝构造函数也必须唯一确定4.析构函数可以为虚函数运行时多态必须用虚函数回收因为实际回收的空间是子类的空间5.inline函数不能为虚函数override编译器会强制检查是否正确重写class Person { private: int pid; char* name; public: Person(int pid, const char* name) :pid(pid) { this-name new char[32] {0}; strcpy(this-name, name); } ​ virtual void hi() { cout Pid: pid , Name: name endl; } ​ virtual ~Person() { cout ~Person() endl; delete[] name; } }; ​ class Student :public Person { private: char* clsname; public: Student(int pid, const char* name,const char* clsname) :Person(pid,name) { this-clsname new char[32] {0}; strcpy(this-clsname, clsname); } ​ void hi() { Person::hi(); cout clsName: clsname endl; } ​ ~Student() { cout ~Student() endl; delete[] clsname; } }; ​ int main() { Person* p new Student(1001, Lucy, c2502); p-hi(); delete p; return 0; }Pid: 1001, Name: Lucy clsName: c2502 ~Student() ~Person()D:\C\x64\Debug\C.exe (进程 32516)已退出代码为 0。 按任意键关闭此窗口. . .5.基类的指针或引用指向派生类的对象:6.运行时多态执行速度会稍慢些。7.虚函数声明和定义分开时只需要在声明上加virtual关键字。禁止在外部定义时加此virtual关键字。多态的实现原理运行时多态的原理虚函数个数和虚函数表里面的个数不一样虚函数指针指向虚函数表确定了实际虚函数的位置虚函数表实际上是函数指针数组里面存储虚函数地址抽象类和接口类抽象类定义包含纯虚函数的类纯虚函数 是没有函数体的只声明接口不实现virtual 返回类型 函数名参数表0特点不能实例化不能创建对象实例派生类未实现纯虚函数时仍是抽象类子类必须重写全部的纯虚函数有一个没实现他就是抽象类可以有构造函数、普通成员函数、成员变量接口类定义类中所有的函数(除了构造函数)都是纯虚函数且没有任何成员变量。特点没有任何成员变量所有函数除构造都是纯虚函数完全用来定义 “规范 / 协议 / 接口”子类必须实现所有接口专门用于约束行为、统一规范