前几天和另一个同事聊天,正好聊到了c++编程语言的的三大特性:封装、继承和多态;当时同事随口提起多态的运行机制,由于之前对这方面没有完全理解,所以回来就做了一个实验,以加深对多态机制的印象,顺便记录下来,以便后面还可以review;
多态的概念:
什么是多态?
官方解释为“在不同继承关系的类对象,去调同一函数,产生了不同的行为”;
我个人认为,“
多态是在继承的基础上,父类和子类同时实现某个使用virtual修饰的方法,在程序运行时,通过动态解析父类指针指向的实际对象,来确定调用的具体实现
”;
多态的实现:
在多态的实现中,需要有类的继承关系,并且父类中的某些方法需要使用关键字virtual修饰,且在子类中可以重写这部分方法;在调用时,当使用父类指针去调用这些使用了virtual修饰的方法时,会根据当前父类指针指向的对象类型来确定调用哪个类的方法;如果一个方法没有使用virtual关键字修饰,且在父类和子类中都有实现,当调用该方法时,会根据指针的类型来确定调用的类;
从上面截图中,我们可以看出来,CParent和CSon具有继承关系,CParent是父类,CSon是子类;在父类中,Print方法没有使用virtual修饰,而Display方法使用了virtual修饰,在调用的时候,使用父类指针指向子类对象,当调用Print方法时,调用的是父类的方法,而Display调用的又是子类的方法;这就是映证了刚才上面介绍的调用经过;为什么会出现使用了virtual修饰的方法会根据指针所指的对象来确定,而没有使用virtual修饰的方法就是根据指针的类型来判定的呢?让我们来看一下c++针对于有无关键字virtual修饰的方法的处理方式
多态运行机制:
在c++程序中,确定具体调用的函数有两种方式,一种称之为
静态绑定
,与之对应的便是
动态绑定
,他是多态实现的基础。
静态绑定
:和C语言编译时确定函数调用一样,在程序编译时就确定调用的是哪个函数,在编译的二进制文件中,将确定了被调用函数的地址,所以在运行的过程中直接访问指定地址就可以,这种方式在运行时非常的高效;
动态绑定
: 是指在程序运行过程中,需要根据业务的运行,调整对象实例,根据对象的类型来确定调用的具体是哪个对象的方法;动态绑定是基于函数指针的基础上加以实现的;类定义的时候,在基类中使用关键字vitrtual修饰方法,在程序运行的时候,系统会为使用了virtual关键字修饰的基类和子类都生成一个虚函数列表,该表中存储了所有的使用关键字virtual修饰的方法的地址,使用这些包含virtual修饰方法的类生成对象的中会有一个虚指针,用以指向虚函数列表;
在程序运行的时候,当使用父类指针指向子类对象的时候,系统会使用在当前指针指向的对象的虚指针,找到对应的虚函数列表,然后根据调用的函数指针类型以及给定的参数等信息,匹配到对应的函数方法,这样就能够完成调用
刚才上面提到了在父类和子类中,当调用没有使用关键字virtual修饰的方法时,会根据生命的指针类型来确定调用的具体方法,这是为什么呢?由于子类继承了父类,所以子类中即包含有父类定义的属性也包含有自己特有的;没有使用virtual修饰的方法,在编译的时候就直接确定了调用函数的地址,所以,在调用时,会出现根据定义的指针类型来确定调用函数的现象;