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

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

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






为'Shape'  的物体呢)。如果硬要强渡关山,会换来这样的编译消息: 



    error  : illegal attempt to instantiate abstract class。 



关于抽象类别,我还有一点补充。CCircle 继承了CShape 之后,如果没有改写CShape  中 



的纯虚拟函数,那么CCircle 本身也就成为一个拥有纯虚拟函数的类别,于是它也是一 



个抽象类别。 



是对虚拟函数做结论的时候了: 



     

■ 如果你期望衍生类别重新定义一个成员函数,那么你应该在基础类别中把此函 



   数设为virtual 。 



                                   Polymorphism      〃the ability to 

■ 以单一指令唤起不同函数,这种性质称为                          ,意思是 



      assume many forms〃,也就是多态。 



            C++      Polymorphism  

■ 虚拟函数是         语言的             性质以及动态绑定的关键。 



                                                                            76 


…………………………………………………………Page 139……………………………………………………………

    ■ 既然抽象类别中的虚拟函数不打算被调用,我们就不应该定义它,应该把它设 



       为纯虚拟函数(在函数声明之后加上〃=0〃  即可)。 



    ■ 我们可以说,拥有纯虚拟函数者为抽象类别(abstract Class ),以别于所谓的 



       具象类别(concrete class) 。 



    ■ 抽象类别不能产生出对象实体,但是我们可以拥有指向抽象类别之指针,以便 



       于操作抽象类别的各个衍生类别。 



    ■ 虚拟函数衍生下去仍为虚拟函数,而且可以省略virtual 关键词。 



类别与对象大解剖 



    你一定很想知道虚拟函数是怎么做出来的,对不对? 



    如果能够了解C++ 编译器对于虚拟函数的实现方式,我们就能够知道为什么虚拟函数 



    可以做到动态绑定。 



    为了达到动态绑定(后期绑定)的目的,C++ 编译器透过某个表格,在执行时期「间接」 



    调用实际上欲绑定的函数(注意「间接」这个字眼)。这样的表格称为虚拟函数表(常 



    被称为vtable )。每一个「内含虚拟函数的类别」,编译器都会为它做出一个虚拟函数表, 



    表中的每一笔元素都指向一个虚拟函数的地址。此外,编译器当然也会为类别加上一项 



    成员变量,是一个指向该虚拟函数表的指针(常被称为vptr )。举个例: 



        class Class1 { 



             public  : 



             data1; 



             data2; 



             memfunc (); 



             virtual vfunc1(); 



             virtual vfunc2 (); 



             virtual vfunc3(); 



        }; 



    Class1 对象实体在内存中占据这样的空间: 



                                                                        77 


…………………………………………………………Page 140……………………………………………………………

                                         Class1 对象实体               vtable 



                                              vptr               (*vfunc1)()           Class1::vfunc1() 

                    class Class1            m_data1              (*vfunc2)()           Class1::vfunc2() 

                    { 

                      public :              m_data2              (*vfunc3)()           Class1::vfunc3() 

                         m_data1; 

                         m_data2;                                                      Class1::memfunc() 

                         memfunc(); 

                         virtual vfunc1(); 

                         virtual vfunc2(); 

                         virtual vfunc3(); 

                    } 



                   C++                                C  

                       类别的成员函数,你可以想象就是  语言中的函数。它只是被编译器改过名称, 



                   并增加一个参数(this 指针),因而可以处理调用者(C++ 对象)中的成员变量。所以, 



                   你并没有在Class1 对象的内存区块中看到任何与成员函数有关的任何东西。 



                  每一个由此类别衍生出来的对象,都有这么一个vptr 。当我们透过这个对象调用虚拟函 



                  数,事实上是透过vptr 找到虚拟函数表,再找出虚拟函数的真正地址。 



                   奥妙在于这个虚拟函数表以及这种间接调用方式。虚拟函数表的内容是依据类别中的虚 



                   拟函数声明次序,一一填入函数指针。衍生类别会继承基础类别的虚拟函数表(以及所 



                   有其它可以继承的成员),当我们在衍生类别中改写虚拟函数时,虚拟函数表就受了影 



                   响:表中元素所指的函数地址将不再是基础类别的函数地址,而是衍生类别的函数地址。 



                   看看这个例子: 



                       class Class2 : public Class1 { 

                         public : 

                           data3; 

                           memfunc(); 

                           virtual vfunc2(); 

                       }; 



78 


…………………………………………………………Page 141……………………………………………………………

 class Class2 : public Class1 

 {                                                   vtable 

   public :                     vptr                (*vfunc1)()           Class1::vfunc1() 

     m_data3; 

     memfunc();               m_data1               (*vfunc2)()           Class2::vfunc2() 

     virtual vfunc2(); 

 }                            m_data2               (*vfunc3)()           Class1::vfunc3() 



                              m_data3                                     Class2::memfunc() 



                          Class2 对象实体 



于是,一个「指向Class1 所生对象」的指针,所调用的vf unc2 就是Class1::vf unc2 ,而 



一个「指向Class2 所生对象」的指针,所调用的vf unc2 就是Class2::vf unc2 。 



动态绑定机制,在执行时期,根据虚拟函数表,做出了正确的选择。 



我们解开了第二道神秘。 



 口说无凭,何不看点实际。观其地址,物焉C哉,下面是一个测试程序: 



#0001  #include  

#0002  #include  

#0003 

#0004  class ClassA 

#0005  { 

#0006  public: 

#0007  int m_data1; 

#0008  int m_data2; 

#0009  void func1() {   } 

#0010  void func2() {   } 

#0011  virtual void vfunc1() {     } 

#0012  virtual void vfunc2() {     } 

#0013  }; 

#0014 

#0015  class ClassB : public ClassA 

#0016  { 

#0017  public: 

#0018  int m_data3; 

#0019  void func2() {  } 

#0020  virtual void vfunc1() {   } 

#0021  }; 

#0022 

#0023  class ClassC : public ClassB 

#0024  { 



                                                                                                79 


…………………………………………………………Page 142……………………………………………………………

                  #0025  public: 

                  #0026  int m_data1; 

                  #0027  int m_data4; 

                  #0028  void func2() {  } 

                  #0029  virtual void vfunc1() {    } 

                  #0030  }; 

                  #0031 

                  #0032  void main() 

                  #0033  { 

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