概念:
就是多个不同的对象,在完成某种相同动作时,会产生多种不同的状态。
就比如:一个音乐播放软件,会员和普通用户 能听的歌不一样,有些歌只有会员可以听,会员也可以选择听更好的音质。
class A { public: virtual void dunc() { cout << "Hello A" << endl; } int a; }; class B : public A { public: virtual void dunc() { cout << "Hello B" << endl; } int b; }; class C : public A { public: virtual void dunc() { cout << "Hello c" << endl; } int c; }; void func(A* ptr) { ptr->dunc(); } int main() { A a; B b; C c; func(&a); func(&b); func(&c); return 0; }
多态有两个要求:
子类虚函数重写父类的虚函数(重写:函数名+返回值+参数相同、是虚函数)
由父类的指针或者引用去调用虚函数
特例:
协变:
class S {}; class W :public S {}; class A { public: virtual S* dunc() { S s; return &s; } int a; }; class B : public A { public: W* dunc() { W w; return &w; } int b; };
只要父子关系的指针和引用做返回值都算 协变
子类虚函数没有写 virtual 父类虚函数写了是没有问题的
class A { public: ~A() { cout << "delete A" << endl; } int a; }; class B : public A { public: ~B() { cout << "delete B" << endl; } int b; }; int main() { A* x = new A; delete x; A* y = new B; delete y; return 0; }
大家觉得这段代码的运行结果是什么
普通调用就是正常的:
x->destructor() + operator delete(x)
y->destructor() + operator delete(y)
你这个变量是什么类型,就调用谁的析构函数。那如果加上virtual呢
class A { public: virtual ~A() { cout << "delete A" << endl; } int a; }; class B : public A { public: ~B() { cout << "delete B" << endl; } int b; }; int main() { A* x = new A; delete x; A* y = new B; delete y; return 0; }
很明显这里就是一个多态调用了,先delete A 然后在子类的析构函数里面,先析构子类再析构父类 。
经过这也就得出一个结论:如果设计一个类,它可能作为基类的话,就需要给基类的析构函数添加上virtual 这样可以防止子类里面的资源泄露
final:
如果有不想要被继承或者被重写的类或者函数,就可以在类名 和 函数后面加上final
1. void func() final {} 2. 3. class A final 4. { 5. };
override:
这个是用来放在子类里面去使用的,用于检查重写(函数名 + 参数 + 返回值)的完整性
抽象类、纯虚函数:
纯虚函数:在虚函数后面写上 =0 ,则这个函数为纯虚函数
抽象类:包含纯虚函数的类叫做抽象类
抽象类不能实例出对象。被继承后派生类也不能实例出对象,只有派生类重写虚函数,才能实例化出对象,纯虚函数也表明派生类必须重写,也体现出了接口继承
查表:
一个指针是不知道自己指向的是 派生类切割过来的 还是 指向自己类型 。所以在调用虚函数的时候会去查对应的虚函数表
class A { public: virtual void dunc() { cout << "Hello A" << endl; } virtual void dunc1() { cout << "dunc1" << endl; } int a; }; class B : public A { public: virtual void dunc() { cout << "Hello B" << endl; } void dunc2() { cout << "dunc2" << endl; } int b; }; int main() { A X; B y; return 0; }
大家仔细地看这段代码,和这两个变量的内容:
首先,这两个虚函数表的地址是不同的;
A类里面的dunc1 是虚函数但是并没有在B类中重写,依然也是在A类的虚函数表里面的;
B类里面的虚函数表里面的第一个函数已经展现不清楚了,但我们依然知道它是重写后的dunc;
动态绑定:
静态绑定又叫前期绑定,在程序编译期间就确定了程序的行为:静态多态,比如:函数重载
动态绑定又叫后期绑定,在程序运行期间,根据具体拿到的类型确定程序的具体行为:动态多态