友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!阅读过程发现任何错误请告诉我们,谢谢!! 报告错误
一世书城 返回本书目录 我的书架 我的书签 TXT全本下载 进入书吧 加入书签

深入浅出MFC第2版(PDF格式)-第29章

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!






        CSales aSales(〃侯俊杰〃); 



        CSales* pSales; 



        CWage* pWager; 



        pSales = &aSales; 



        pWager = &aSales; // 以「基础类别之指针」指向「衍生类别之对象」 



        pWager…》setSales(800。0); //                  ) 

                                   错误(编译器会检测出来 , 



        // 因为CWage 并没有定义setSales 函数。 



        pSales…》setSales(800。0); // 正确,调用CSales::setSales 函数。 



       pSal es  pWage r  

    虽然       和       指向同一个对象,但却因指针的原始类型而使两者之间有了差异。 



    延续此例,我们看另一种情况: 



        pWager…》putePay (); // 调用CWage::putePay () 



        pSales…》putePay (); // 调用CSales::putePay () 



       pSal es  pWage r         CSales                  put ePay  

    虽然       和       实际上都指向          对象,但是两者调用的                  却不 



                                                                                68 


…………………………………………………………Page 131……………………………………………………………

    相同。到底调用到哪个函数,必须视指针的原始类型而定,与指针实际所指之对象无关。 



三个结论 



    我们得到了三个结论: 



    1。 如果你以一个「基础类别之指针」指向「衍生类别之对象」,那么经由该指针 



      你只能够调用基础类别所定义的函数。 



        class CBase                      CBase* pBase; 



        BaseFunc() 



                                 虽然我们可以令pBase 实际指向CD erived 对象, 



                                 却因为pBase 的类型(〃一个CBase* 指针〃) 

      class CDerived 

                                 使它只能够调用BaseFunc(),不能够调用DeriFunc()。 

        DeriFunc() 

     



    2。 如果你以一个「衍生类别之指针」指向一个「基础类别之对象」,你必须先做 



      明显的转型动作(explicit cast )。这种作法很危险,不符合真实生活经验,在 



      程序设计上也会带给程序员困惑。 



        class CBase       CDerived *pDeri; 

                          CBase aBase(〃Jason〃); 

        BaseFunc() 

                                             这种作法很危险,不符合真实生活经验, 

                          pDeri = &aBase;  //  

                                           //  

                                              在程序设计上也会带给程序员困惑。 



      class CDerived                  CDerived* pDeri; 



        DeriFunc() 

     



    3。 如果基础类别和衍生类别都定义了「相同名称之成员函数」,那么透过对象指 



      针调用成员函数时,到底调用到哪一个函数,必须视该指针的原始型别而定, 



      而不是视指针实际所指之对象的型别而定。这与第  点其实意义相通。 

                                              1  



                                                                                   69 


…………………………………………………………Page 132……………………………………………………………

                       class CBase 

                                         CBase* pBase; 

                        BaseFunc() 

                                         CDerived* pDeri; 

                        mFunc() 



                                           不论你把这两个指针指向何方,由于它们的原始类型, 

                      class CDerived       使它们在调用同名的 m Func() 时有着无可改变的宿命: 



                                           o pBase…》mFunc() 永远是指CBase::mFunc 

                        DeriFunc() 

                        mFunc()         o pDeri…》mFunc() 永远是指CDerived::mFunc 



                 得到这些结论后,看看什么事情会困扰我们。前面我曾提到一个由职员组成的串行,如 



                             pr intNames  

                  果我想写一个              函数走访串行中的每一个元素并印出职员的名字,我们可以在 



                  CEmpl oyee                     getName                   while  

                           (最基础类别)中多加一个                  函数,然后再设计一个             循环如下: 

                      int count = 0; 

                     CEmployee* pEmp; 

                      。。。 

                     while (pEmp = anIter。getNext()) 

                      { 

                        count++; 

                        cout  put ePay while  

    因此如果上述       循环中调用的是                   ,那么     循环所执行的将 



    总是相同的运算,也就是CEmpl oyee::put ePay ,这就糟了(销售员领到经理的薪水还 



    不糟吗)。更糟的是,我们根本没有定义CEmpl oyee::put ePay ,因为CEmpl oyee  只 



    是个抽象概念(一个抽象类别)。指针必须落实到具象类型上如CWage 或CManager 或 



    CSales ,才有薪资计算公式。 



虚拟函数与一般化 



    我想你可以体会,上述的while 循环其实就是把动作「一般化」。「一般化」之所以重 



    要,在于它可以把现在的、未来的情况统统纳入考量。将来即使有另一种名曰「顾问」 



    的职员,上述计薪循环应该仍然能够正常运作。当然啦,「顾问」的put ePay 必须设 



    计好。 



     「一般化」是如此重要,解决上述问题因此也就迫切起来。我们需要的是什么呢?是能 



    够「依旧以CEmpoly ee 指针代表每一种职员」,而又能够在「实际指向不同种类之职员」 



    时,「调用到不同版本(不同类别中)之put ePay 」这种能力。 



    这种性质就是多态(polymorphism ),靠虚拟函数来完成。 



    再次看看那张计薪循环图: 



       pE mp             pE mp…》 put ePay  

    ■ 当     指向经理,我希望                   是经理的薪水计算式,也就是 



         CManager::put ePay 。 



       pE mp               pE mp …》put ePay  

    ■ 当     指向销售员,我希望                    是销售员的薪水计算式,也就 



       是CSales ::put ePay 。 



       pE mp                pE mp …》put ePay  

    ■ 当     指向时薪职员,我希望                    是时薪职员的薪水计算式, 



       也就是CWage::put ePay 。 



    虚拟函数正是为了对「如果你以一个基础类别之指针指向一个衍生类别之对象,那么透 



    过该指针你就只能够调用基础类别所定义之成员函数」这条规则反其道而行的设计。 



                                                                        71 


…………………………………………………………Page 134……………………………………………………………

                    不必设计复杂的串行函数如add 或getNext 才能验证这件事,我们看看下面这个简单例 



                    子。如果我把职员一例中所有四个类别的put ePay  函数前面都加上virtual 保留字, 



                    使它们成为虚拟函数,那么: 



                      CEmployee* pEmp; 

                      CWage      aWager(〃曾銘源〃); 

                      CSales     aSales(〃侯俊傑〃); 

                      CManager   aManager(〃陳美靜〃); 



                     
返回目录 上一页 下一页 回到顶部 0 1
未阅读完?加入书签已便下次继续阅读!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!