一、多态的概念
多态就是多种状态,比如打印机可以打印黑白 也可以打印彩的
买票买学生票或者成人票或者儿童票,也可以逃票~~~
二、多态的定义和实现
实现多态的两个必要条件!!
必须是基类的指针或者引⽤调⽤虚函数被调⽤的函数必须是虚函数,并且完成了虚函数重写/覆盖。
#include <iostream> using namespace std; class Person { public: virtual void BuyTicket() { cout << "买票-全价" << endl; } }; class Student : public Person { public: virtual void BuyTicket() { cout << "买票-打折" << endl; } }; void Func(Person* ptr) { // 这⾥可以看到虽然都是Person指针Ptr在调⽤BuyTicket // 但是跟ptr没关系,⽽是由ptr指向的对象决定的。 ptr->BuyTicket(); } int main() { Person ps; Student st; Func(&ps); Func(&st); return 0; }那啥是重写和覆盖呢???
虚函数的重写/覆盖:派⽣类中有⼀个跟基类完全相同的虚函数(即派⽣类虚函数与基类虚函数的返回值 类型、函数名字、参数列表完全相同),称派⽣类的虚函数重写了基类的虚函数。
注意:在重写基类虚函数时,派⽣类的虚函数在不加virtual关键字时,虽然也可以构成重写(因为继承后基类的虚函数被继承下来了在派⽣类依旧保持虚函数属性),但是该种写法不是很规范,不建议这样使⽤,不过在考试选择题中,经常会故意买这个坑,让你判断是否构成多态。
class Student : public Person { public: private: virtual void BuyTicket() { cout << "买票-打折" << endl; } };甚至这样也是可以调用的
是因为,多态发生,派生类使用的是基类的声明 所以private还是public就无所谓了~
重写的一些其他问题 协变
class A { }; class B : public A { }; class Person { public: virtual A *BuyTicket() { cout << "买票-全价" << endl; return nullptr; } }; class Student : public Person { public: virtual B *BuyTicket() { cout << "买票-打折" << endl; return nullptr; } }; void Func(Person *ptr) { ptr->BuyTicket(); } int main() { Person ps; Student st; Func(&ps); Func(&st); return 0; }析构函数的重写
因为多态的原因,析构函数的名字变成了destructor() 这样两个类的继承的析构就变成了隐藏
class A { public: virtual ~A() { cout << "~A()" << endl; } }; class B : public A { public: ~B() { cout << "~B()->delete:" << _p << endl; delete _p; } protected: int *_p = new int[10]; }; // 只有派⽣类Student的析构函数重写了Person的析构函数,下⾯的delete对象调⽤析构函数,才能 // 构成多态,才能保证p1和p2指向的对象正确的调⽤析构函数。 int main() { A *p1 = new A; A *p2 = new B; delete p1; delete p2; return 0; }这样才不会造成内存泄漏!!! 多态调用,指向谁,调用谁的析构函数!!
基类的析构函数一定要是虚函数!!!
重载 重写 隐藏的对比
纯虚函数和抽象类
纯虚函数1. 不能实例化对象2. 强制子类去重写虚函数
class Car { public: virtual void Drive() = 0; }; class Benz :public Car { public: virtual void Drive() { cout << "Benz-舒适" << endl; } }; class BMW :public Car { public: virtual void Drive() { cout << "BMW-操控" << endl; } }; int main() { // 编译报错:error C2259: “Car”: ⽆法实例化抽象类 Car car; Car* pBenz = new Benz; pBenz->Drive(); Car* pBMW = new BMW; pBMW->Drive(); return 0; }三、多态的原理
看下列代码,运行结果是什么?64位
class Base { public: virtual void Func1() { cout << "Func1()" << endl; } // virtual void Func2() // { // cout << "Func2()" << endl; // } protected: int _b = 1; char _ch = 'x'; }; int main() { Base b; cout << sizeof(b) << endl; return 0; }因为会存一个虚函数表指针 64位 是8个字节
虚函数表,简称虚表,本质是函数指针数组!!!
class Person { public: virtual void BuyTicket() { cout << "买票-全价" << endl; } private: string _name; }; class Student : public Person { public: virtual void BuyTicket() { cout << "买票-打折" << endl; } private: string _id; }; class Soldier : public Person { public: virtual void BuyTicket() { cout << "买票-优先" << endl; } private: string _codename; }; void Func(Person* ptr) { // 这里可以看到虽然都是Person指针ptr在调用BuyTicket // 但是跟ptr没关系,而是由ptr指向的对象决定的。 ptr->BuyTicket(); } int main() { // 其次多态不仅仅发生在派生类对象之间,多个派生类继承基类,重写虚函数后 // 多态也会发生在多个派生类之间。 Person ps; Student st; Soldier sr; Func(&ps); Func(&st); Func(&sr); return 0; }满足多态,运行时到指向对象虚函数表中找到对应虚函数调用!!
3.1 动态绑定与静态绑定
3.2 虚函数表
同一个类的所有对象共享同一份虚函数表
同时派生类会继承基类的虚函数表,然后根据已有的进行覆盖和添加!!