今天主要是想发表一下自己对C++语言在实现多态方面的做法:
众所周知,多态指统一与特殊的一个概念,把共性作为更高层次的抽象,基于上层抽象进行个性化的扩展。也就是一个基于通用的接口类或者说抽象类派生多个或多层子类(动态多态)
在C++里边只要表现为静态多态及动态动态,所谓静态多态就是指模板或构造函数这些形式,最显著的特征是它们的多样性在编译的时候被决定下来。这就是静态多态。
至于动态多态的理解其实是这样的,它在编译的时候做了一些处理,实质上对于一个编程人员而言,结果在我编写程序的时候就已经知道了。但在语言这个层面上,多态的真正表现发生到程序运行的时候。最显著的特征就是你明明用一个基类的指针或引用去调用虚函数,按道理它是执行此类型内的函数才是,结果它执行了子类的虚函数。当然这恰恰也是我们希望的一种结果,这既是一个奇妙的地方,也是动态多态的需求背景。正因为我们编程上需要这样一种效果——动态多态,所以语言的创造者们绞尽脑汁将其设计在语言当中——程序员主要依照它的规则进行程序编写就能达到动态的效果——前提我们设计软件的时候客观地存在这种需求——多态——动态多态。
显然C++是通过“指针或引用调用虚函数”这种语法来向编译器表达这种功能的。一旦满足了“指针或引用调用虚函数”这样的写法,编译器就准备了动态多态出现的所以技术实现。
做法如下:因为指针或引用的指向是任意的,它代表的实际类型显然是编译时未必能决定的,它随时可能在程序执行的过程中被传参,赋值,也就是说它的真实指向是运行时决定的,这就意味着这种做法是适合实现动态多态的。如果是对象本身到调用虚函数,对象代表就是本身,所以不会用运行时也不可能在运行时才决定下来,所以对象调用虚函数是不能实现多态的。
在C++ 面向对象编程的时候,有两个点是要注意到的:1、所有非静态成员函数都要通过对象调用,因为封装其实不过是数据与函数的封装,函数调用必要地传递数据,就像C中的一批处理某个结构体的函数一样,C+采用了隐藏传递。2、按在上述的规定调用方式,如果说出现语法“指针或引用调用虚函数”,那么 编译处理是“除了必然的参数传递外,这个函数并非直接被调用,而是从左边指针或引用中索引它的虚函表——通过其指向或引用的对象空间中虚函数表指针出发,查表调用它右边的函数。当然真正发起调用时传递的参数也是左边的对象。
所以在C++中只要出现 指针或引用 调用虚函数 的情况就会进行动态多态编译技术处理。然后由运行时指针或引用的实际指向决定虚函数的调用。原理很简单:指针或引用调用 虚函数是通过 指向或引用的对象空间中的虚函数表指针出发,查表调用它右边的函数的。当然例外的是构造函数调用虚函数不作动态多态编译处理——毕竟子类未初始化,子类对象空间的虚函数表指针未被初始化——它的初始化理应在轮到它的构造函数被调用时才开始。当然这个只是猜想,毕竟单继承中,虚函数表指针是继承下来的,它的初始是最开始呢?还是等到子类构造函数开始阶段呢?当然这不会影响到构造函数调虚函数不做多态处理的做法。因为在基类初始的未结束——即子类都未初始化,就去调用子类的方法——这种做法是不严谨的,子类的成员数据未确定可能会带来程序上的灾难。