多态性(Polymorphism)是初学者最难理解的C++特性之一,“多态性”这个词似乎有点怪异,也许这就是令人难懂的原因吧,其实这个特性完全可以用别的方法描述(如以后将采用的实现机制描述),这样可以更直接的了解其本质。程序员们是一群很怪的家伙,做事情总是采取难理解的方法,有时甚至只是为好玩而使事情搞得很复杂。C语言程序员甚至可以参加一种年度竞赛,谁写的程序最难懂,谁就得一等奖。程序设计语言是通过书、杂志和论文等方式加以推广的,感兴趣的人越多越有利于推广。听起来很怪异的词更可能引起人们的兴趣,促使人们研究语言的新特性。如果有足够多的人发生了兴趣,语言使用者的数量达到了一定程度,该语言就成功了。这就是C++语言的演化过程,也许多态性这个术语在增加语言使用者方面起到了一定的作用。
多态性是指C++的代码可以根据运行情况的不同执行不同的操作。这通常不是由此代码的编写程序员直接控制的,而必须根据此代码的最终衍生情况,依靠C++对象对自身进行跟踪,最终确定相关的操作。与其说多态性是类的特性,不如说它是类成员函数的特性。多态性是通过类的体系结构来实现的,不过只有类的成员函数可以具有多态性,而不是整个类都具有相关特性。下面分几个方面逐步阐述相关的概念和实现。
为何要用多态性
先看一下在封装中所提到的那个状态机的直接包装程序:
enum status_type { S0, S1, S2, S3, S4,…,Sn }; class class_SM { protected: status_type status; virtual int Fy( int x ); virtual void Fs( int x ); public: class_SM( void ) { status = S0; } class_SM( status_type S ) { status = S; } int Do_it( int x ); ~class_SM() {} };
int class_SM :: Fy( int x ) { // return Fy( x, status )
return x; }
void class_SM :: Fs( int x ) { // status <- Fs( x, status ) }
int class_SM :: Do_it( int x ) { int result; result = Fy( x ); Fs( x ); return result; }
现在去掉关键字virtual,采用形式化的描述将程序形式简化如下:
class class_SM { Fy; Fs; Do_it { Fy; Fs; } };
其中Fy和Fs是类class_SM中定义的两个成员函数。而Do_it同样也是class_SM的一个成员函数,不同的是其还调用了函数Fy和Fs。在论述继承时,给出了相应的派生类,现在同样用形式化的描述再现如下。
class class_My_SM1 : public class_SM { Fy; Fs; };
其中在类class_My_SM1中,对成员函数Fy和Fs进行了重新定义。由于class_SM是其基类,所以其基类中的成员函数Do_it被原封不动的继承了下来。如果采用前面说过的类成员函数的简单继承和覆盖概念,class_My_SM1虽然自定义了两个新的成员函数class_My_SM1::Fy和class_My_SM1::Fs,但最终却是徒劳。因为其继承来的成员函数Do_it所调用的还是基类的成员函数class_SM::Fy和class_SM::Fs。为了能够使得继承而来的成员函数调用类中新定义的Fy和Fs,就必须引入一种全新的调用机制。为了区别于一般的成员函数的调用,在必须采用特殊调用机制的成员函数前加上一个特殊的关键字——virtual,相关函数被称之为虚函数。采用虚函数后,上面例子的形式化描述再表如下:
class class_SM { virtual Fy; virtual Fs; Do_it { Fy; Fs; } };
class class_My_SM1 : public class_SM { virtual Fy; virtual Fs; };
class_My_SM1 My_SM1;
由于引入了多态调用机制,对象My_SM1的成员函数Do_it将调用所属类class_My_SM1中重新定义的虚函数Fy和Fs,这样才真正实现了其功能的更新。
这里要进一步重申的是,虚成员函数(简称虚函数)一般是被类中其他成员函数所调用的。反过来说,如果一个成员函数只是作为顶层函数出现,就没有必要将其归入虚函数之列,虽然在派生类中可能会对它进行覆盖。 |