c++-多态
多态介绍买车票时学生是半价军人要优先......在同一个售票窗口不同的人会享受到不同折扣这是多态的一个现实例子。对于同一操作有时候我们需要根据对象类型的不同产生不同行为这就是多态。多态又分为静态多态静态绑定和动态多态动态多态。静态多态典型的例子就是函数重载参数列表不同调用到的重载函数也不同结果也就不同。之所以叫静态多态是因为在编译期间根据实参就已经确定了实际要调用的函数的地址。动态多态是指在程序运行中程序才会确定要调用的函数地址。本文讨论多态类型的就是动态多态。多态的实现首先看一下多态如下图不同的对象作为实参调用同一个函数表现出了不同的行为class person { public: virtual void sales() { cout 正常买票 endl; } private: }; class student :public person { public: virtual void sales() { cout 买学生票打5折 endl; } private: }; class solider :public person { public: virtual void sales() { cout 买军人票打1折 endl; } private: }; void func(person x) { x.sales(); } int main() { solider x; student y; person z; func(x); func(y); func(z); }接下来是实现多态的步骤在父类中找到想要呈现出多态的函数图中是sales函数用virtual修饰。被virtual修饰的函数叫做虚函数。在各个子类中重写父类的虚函数要求函数名返回值参数必须相同子类中的virtual可加可不加就算不加子类重写的函数也是虚函数但是父类中的virtual一定要加表示这个函数将被重写。用父类的引用或者指针接收不同子类对象func函数的形参就是父类的引用使用父类的引用或者指针调用父类虚函数这样根据引用对象的不同调用到的函数实现也不同。使用多态的注意事项1.多态条件下在子类中重写函数后重写的是函数的实现可以看成重写后的函数的函数名参数返回值都用的是父类的。来看一个例子class person { public: virtual void sales(int x 6) { cout x endl; } private: }; class student : public person { public: virtual void sales(int x 5) { cout x endl; } private: }; int main() { student st; person p st; p.sales();//打印的是6而不是5因为函数签名仍是用父类的而父类的默认参数是6 }2.如果虚函数声明和定义分离在类外定义时不要加virtualvirtual只用于声明。3. 没有虚静态成员函数4.关于多态的原理我相信看完这篇文章读者会深刻理解揭秘C对象模型虚表与内存布局-CSDN博客https://blog.csdn.net/2302_80221042/article/details/149544880?spm1001.2014.3001.5501虚函数重写的两个例外协变基类返回值与子类返回值不同派生类重写基类虚函数时与基类虚函数返回值类型不同即基类虚函数返回基类对象的指针或者引用派生类虚函数返回派生类对象的指针或者引用时同样构成重写这是c对虚函数重写规则的例外放宽。下面是一个例子class Base { public: virtual Base* func() { cout virtual Base* func() endl; return this; } }; class A : public Base { public: virtual A* func() { cout virtual A* func() endl; return this; } }; int main() { A a; Base* p a; p-func(); }结果是析构函数的重写(基类与派生类析构函数的名字不同)如果基类的析构函数为虚函数此时派生类析构函数只要定义就与基类的析构函数构成重写虽然基类与派生类析构函数名不相同。这看起来违背了重写的规则其实不然这里可以理解为编译器对析构函数的名称做了特殊处理编译后析构函数的名称统一处理成destructor。下面是一个例子class Base { public: virtual ~Base() { cout virtual ~Base() endl; } }; class A : public Base { public: virtual ~A() { cout virtual ~A() endl; } }; int main() { Base* p new A; delete p; }结果是上述过程就是delete p调用的是A的析构函数而A的析构函数调用结束后会默认调用Base的析构函数这是继承体系中析构函数的特性。在实现多态时最好把析构函数也声明为虚函数因为如果析构函数不声明为虚函数可能会导致内存泄漏子类析构函数没有被调用class Base { public: ~Base() { cout ~Base() endl; } }; class A : public Base { public: ~A() { cout ~A() endl; } }; int main() { Base* p new A; delete p;//由于没有把析构函数声明为虚函数delet调用的只是Base类的析构函数 //如果A类中有需要释放的资源就会导致内存泄漏 }结果为C11 中的 override 和 finalfinal在父类中修饰虚函数表示该虚函数不能再被重写class Base { public: virtual void func()final {} };final也可以修饰整个类表示该类不能被继承class Base final { public: virtual void func() {} };override检查被修饰函数是否是基类某个虚函数的重写如果不是则在编译的时候报错class Base { public: virtual void func() {} }; class A : public Base { public: virtual void func()override { } };抽象类接口类在虚函数的后面写上 0 则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类也叫接口 类抽象类不能实例化出对象。派生类继承后也不能实例化出对象只有重写了所有纯虚函数之后派生类才能实例化出对象。举个例子class Base//抽象类 { public: virtual void func() 0;//纯虚函数 virtual void func2(){}; void func3() { //..... } }; class A : public Base { public: virtual void func()override { } }; int main() { Base base;//报错抽象类不能实例化 A a;//正确因为A类重写了Base中的所有纯虚虚函数 }为什么会有抽象类想象一下基类是人类派生出中国人小日子英国佬等子类说这个人是中国人很合理但是总不能说这个人是人类吧也就是说有时候我们不需要甚至不允许用父类创建对象因此就有了抽象类。