C面向对象核心什么是多态Polymorphism——同一个指令不同对象表现不同对于小学生可以先记一句话多态就是同一个动作让不同的对象表现出不同的结果。例如我们说“动物叫”不同动物狗汪汪叫猫喵喵叫鸭子嘎嘎叫命令一样叫但是结果不同。这就是多态。一、C面向对象三大特点我们之前学习1. 封装把数据和操作放一起。例如class Student { private: int score; };保护数据。2. 继承子类拥有父类功能。例如class Dog:public Animal { };3. 多态同一个接口不同表现。例如Animal | | Dog Cat 叫()调用叫()结果狗汪汪 猫喵喵二、先看没有多态的问题我们写动物class Animal { public: void speak() { cout动物叫endl; } };狗class Dog:public Animal { };猫class Cat:public Animal { };现在Dog d; d.speak();输出动物叫但是我们希望汪汪怎么办三、让子类重新定义函数我们可以class Dog:public Animal { public: void speak() { cout汪汪endl; } };猫class Cat:public Animal { public: void speak() { cout喵喵endl; } };现在Dog d; d.speak();输出汪汪四、但是这还不是多态为什么看Dog d; Cat c; d.speak(); c.speak();编译器知道你是Dog。你是Cat。所以直接调用对应函数。这叫函数重定义隐藏不是多态。五、真正的多态需要什么C多态核心两个条件条件1父类有虚函数virtual条件2用父类指针或引用指向子类对象。六、虚函数实现多态完整代码#includeiostream using namespace std; class Animal { public: virtual void speak() { cout动物叫endl; } }; class Dog:public Animal { public: void speak() { cout汪汪endl; } }; class Cat:public Animal { public: void speak() { cout喵喵endl; } }; int main() { Animal* p; Dog dog; Cat cat; pdog; p-speak(); pcat; p-speak(); }输出汪汪 喵喵七、这里发生了什么重点来了。代码Animal* p;表示一个动物指针。然后pdog;虽然p类型Animal*但是实际指向Dog调用p-speak();C会问p真正指向谁发现Dog于是调用Dog::speak()后来pcat;现在p真正指向Cat于是调用Cat::speak()这就是一个父类指针根据实际对象不同调用不同函数。八、生活中的例子假设学校有老师 | --数学老师 | --英语老师学校系统只保存Teacher*上课teach();数学老师输出讲数学英语老师输出讲英语系统不用知道到底是哪种老师。只需要teacher-teach();这就是多态的价值。九、为什么需要多态假设动物园有100只动物如果不用多态你可能写if(typedog) { dog.speak(); } else if(typecat) { cat.speak(); }动物越来越多代码越来越复杂。有多态统一Animal* animals[100]; animals[i]-speak();每个动物自己决定怎么叫。十、多态的底层秘密虚函数表这是进阶知识。当类有virtual编译器会给对象增加虚函数表指针 vptr例如Dog对象---------------- | vptr | ---------------- | Dog数据 | ----------------vptr指向虚函数表 speak() --- Dog::speak()所以p-speak();程序通过vptr ↓ 虚函数表 ↓ 找到真正函数这叫动态绑定。十一、静态绑定和动态绑定这是考试常考。普通函数void speak()调用时编译阶段决定。叫静态绑定虚函数virtual void speak()运行时决定。叫动态绑定十二、多态和继承的关系多态不是单独存在的。通常继承 虚函数 父类指针 多态十三、为什么析构函数经常写virtual例如class Animal { public: virtual ~Animal() { } };为什么看Animal* pnew Dog; delete p;如果析构函数不是虚函数可能只调用Animal析构Dog自己的资源没有释放。如果virtual ~Animal()会Dog析构 ↓ Animal析构所以有继承关系的父类析构函数通常应该写成虚函数。十四、记忆口诀继承孩子拥有爸爸能力多态同一个命令 不同对象 不同结果C实现多态父类指针 子类对象 virtual函数 多态十五、最后总结一张图Animal | ---------------- | | Dog Cat Animal* p; pdog; p-speak(); ↓ 汪汪 pcat; p-speak(); ↓ 喵喵一句话总结继承解决“是不是一种”的问题组合解决“有没有”的问题多态解决“同一个动作不同对象怎么做不同事情”的问题。掌握多态以后C面向对象三大核心封装 → 继承 → 多态就完整串起来了。