C++中的多态(polymorphism)是指由继承而产生的相关的不一样的类,其对象对同一消息会做出不一样的响应。
多态性是面向对象程序设计的一个重要特征,能增长程序的灵活性。能够减轻系统升级,维护,调试的工做量和复杂度。
多态是一种不一样层次分类下的重要联系,是一种跨层操做。ios
赋值兼容规则是指在须要基类对象的任何地方均可以使用公有派生类的对象来替代。
赋值兼容是一种默认行为,不须要任何的显式的转化步骤,只能发生在public继承方式中,是多态实现的前提条件。
赋值兼容规则中所指的替代包括如下的状况:
A、子类对象能够直接赋值给父类对象
B、子类对象能够直接初始化父类对象
C、父类引用能够直接引用子类对象
D、父类指针能够直接指向子类对象
当使用父类指针(引用)指向子对象时,子类对象退化为父类对象,只能访问父类中定义的成员,能够直接访问被子类覆盖的同名成员。编程
#include <iostream> using namespace std; class Parent { public: int m; Parent(int a) { m = a; } void print() { cout << "Parent m = " << m << endl; } }; class Child : public Parent { public: int m; Child(int a):Parent(a) { m = a; } void print() { cout << "Child m = " << m << endl; } }; int main() { Parent p(0); Child c(0); Parent p1(c); Parent* pp = &c;//指向子类对象的父类指针退化为父类对象 Parent& rp = c;//指向子类对象的父类引用退化为父类对象 pp->print(); //Parent m = 0 rp.print(); //Parent m = 0 //Child cc = static_cast<Child>(p);//须要转换构造函数支持 Child* pc = static_cast<Child*>(&p); pc->print();//Child m = xxxx return 0; }
在替代后,派生类对象就能够做为基类的对象使用,但只能使用从基类继承的成员。
父类也能够经过强转的方式转化为子类,但存在访问越界的风险。
子类中能够重定义父类中已经存在的成员函数,即函数重写。
当函数重写遇到赋值兼容时,编译器只能根据指针的类型判断所指向的对象,根据赋值兼容原则,编译器认为父类指针指向的是父类对象,只能调用父类中定义的同名函数。
面向对象编程中,一般须要根据实际的对象类型判断如何调用重写函数。当父类指针指向父类对象,则调用父类定义的函数;当父类指针指向的是子类对象时,须要调用子类定义的函数;当父类引用对父类对象进行引用时,调用父类对象定义的函数;当父类引用对子类对象进行引用时,调用子类定义的函数。ide
根据父类指针指向的实际对象类型决定调用的函数,即多态。多态中,父类指针(引用)指向父类对象则调用父类中定义的函数,父类指针(引用)指向子类对象时调用子类对象的函数。
C++经过virtual关键字对多态进行支持,被virtual声明的函数被重写后具备多态属性。
多态造成的条件:
A、父类中有虚函数。
B、子类override(覆写)父类中的虚函数。
C、经过己被子类对象赋值的父类指针,调用共用接口。函数
虚函数的声明语法以下:组件化
virtual 函数声明;
虚函数的使用规则以下:
A、在父类中用 virtual 声明成员函数为虚函数。类外实现虚函数时,没必要再加virtual。
B、在派生类中重定义父类中已经存在的成员函数称为函数覆写,要求函数名,返值类型,函数参数个数及类型所有匹配,并根据派生类的须要从新定义函数体。
C、当一个成员函数被声明为虚函数后,其派生类中彻底相同的函数也为虚函数,派生类中的虚函数能够加virtual关键字,也能够不加。
D、定义一个父类对象指针,使其指向其子类的对象,经过父类指针调用虚函数,此时调用的是子类的同名函数。
E、构造函数不能为虚函数,在构造函数执行完毕后虚函数表指针才能被正确初始化。析构函数能够为虚函数,定义一个父类指针并使用new建立的子类对象初始化,使用delete释放父类指针的堆空间时,只会调用父类的析构函数,不会调用子类的析构函数,会形成内存泄漏,父类析构函数声明为虚函数能够避免该问题。通常来讲须要将析构函数声明为虚函数。构造函数执行时,虚函数表指针未被正确初始化,所以构造函数不可能发生多态;析构函数函数执行时,虚函数表指针已经被销毁,所以析构函数也不可能发生多态。构造函数和析构函数中只能调用当前类中定义的函数版本。学习
#include <iostream> using namespace std; class Parent { public: int mi; void add(int i) { mi += i; } void add(int a, int b) { mi += (a + b); } virtual void print() { cout << "Parent" << endl; } }; class Child : public Parent { public: int mi; //函数重写 void add(int x, int y) { mi += (x + y); } //函数重写 void print() { cout << "Child" << endl; } }; int main(int argc, char *argv[]) { Child child; child.add(1,2);//调用子类函数 child.Parent::add(1);//调用父类函数 child.Parent::add(1,2);//调用父类函数 Parent p = child; p.add(1); p.add(1,2); Parent& rp = child; rp.add(1); rp.add(1,2); rp.print();//Child Parent* pp = &child; pp->add(1); pp->add(1,2); pp->print();//Child return 0; }
纯虚函数的声明语法以下:spa
virtual 函数声明 = 0;
纯虚函数的使用规则以下:
A、含有纯虚函数的类,称为抽象基类,不可实例化,即不能建立对象,存在的意义就是被继承,提供类族的公共接口,Java中称为 interface。
B、纯虚函数只有声明,没有实现。
C、若是一个类中声明了纯虚函数,而在派生类中没有对纯虚函数进行实现,则该虚函数在派生类中仍然为纯虚函数,派生类仍然为抽象基类。设计
虚函数的使用规则以下:
A、只有类的成员函数才能声明为虚函数。虚函数仅适用于有继承关系的类对象,因此普通函数不能声明为虚函数。
B、静态成员函数不能是虚函数。静态成员函数不受对象的捆绑,只有类的信息。
C、内联函数不能是虚函数。
D、构造函数不能是虚函数。构造时,对象的建立还没有完成。构造完成后,才能算一个名符其实的对象。
E、析构函数能够是虚函数且一般声明为虚函数
F、含有虚函数的类,析构函数也必须声明为虚函数。在delete父类指针的时候,会调用子类的析构函数。指针
多态的意义以下:
A、在程序运行过程当中展示出的动态特性
B、函数重写必须多态实现,不然没有意义
C、多态是面向对象组件化程序设计的基础特性
D、多态是一种跨层操做
静态联编是在程序的编译期间就能肯定具体的函数调用,如函数重载。
动态联编是在程序实际运行时才能肯定具体的函数调用,如函数重写。调试
#include <iostream> using namespace std; class Parent { public: int mi; virtual void add(int i) { mi += i; } virtual void add(int a, int b) { mi += (a + b); } virtual void print() { cout << "Parent" << endl; } }; class Child : public Parent { public: int mi; //函数重写 void add(int x, int y) { mi += (x + y); } //函数重写 void print() { cout << "Child" << endl; } }; int main(int argc, char *argv[]) { Child child; child.add(1,2);//静态联编 child.Parent::add(1);//静态联编 child.Parent::add(1,2);//静态联编 Parent p = child; p.add(1);//静态联编 p.add(1,2);//静态联编 p.print();//静态联编 cout << endl; Parent& rp = child; rp.add(1);//静态联编 rp.add(1,2);//动态联编 rp.print();//动态联编 Parent* pp = &child; pp->add(1);//静态联编 pp->add(1,2);//动态联编 pp->print();//动态联编 return 0; }