非多态情况 (动态)

来源:互联网 发布:java程序员壁纸 编辑:程序博客网 时间:2024/05/28 23:11

一. 非多态分类:
C++Primer 第三版中文版Page752有如下记载:
“当成员函数是虚拟的时候,通过一个类对象(指针或引用)而被调用的该成员函数,是在该类对象的动态类型中被定义的成员函数。”
这句话有两个元素至关重要,
a.虚成员函数
b.类对象的指针或引用
而此两点有无的组合和有四种情况:
只有a,b同时符合才能实现动态联编(((Father*)lpFson)->DisplayV()结果参考),
而其他三种情况均不符合,编译器在编译期间将确定调用函数。
编译器也是根据此两点来确定到底是否动态联编。
下面对其他三种情况非动态联编进行说明举例。
1)子类覆盖父类函数时,指向子类对象的指针强制转换成指向父类的指针,然后调用此覆盖函数时,不进行动态动态绑定。也就是调用的是父类的函数,而非子类的函数((Father*)lpFson)->Display();结果参考)。【a不符】
2)子类覆盖父类函数时,子类对象强制转换成父类,然后调用此覆盖函数时,不进行动态动态绑定。也就是调用的是父类的函数,而非子类的函数(((Father)son).DisplayV();结果参考)。【b不符】
3)子类隐藏父类函数时,子类对象强制转换成父类,然后调用此隐藏函数时,不进行动态动态绑定。也就是调用的是父类的函数,而非子类的函数(((Father)son).Display();结果参考)。【a,b都不符】      
      
代码验证:

  class Father            
  {              
  public:              
    Father(int i)          
    {             
      var1 = i;        
    }            
  public:              
    ~Father(void){};          
    void Display()          
    {            
      TRACE("Father::Display  var1 = %d/n",var1);  
    }            
    virtual  void DisplayV()        
    {            
      TRACE("Father::DisplayV  var1 = %d/n",var1);  
    }            
                 
  private:              
    int var1;          
  };              
                 
  //class Son            
  class Son : public  Father          
  {              
  public:              
    Son(int i,int j):Father( i)      
    {             
      var2 = j;        
    }            
  public:              
    ~Son(void){};          
    void Display()          
    {            
      TRACE("Son::Display  var2 = %d/n",var2);  
    }            
    virtual  void DisplayV()        
    {            
      TRACE("Son::DisplayV  var2 = %d/n",var2);  
    }            
  private:              
    int var2;          
    int var3;          
  };              
                 
  //调用呼出函数            
  void CTianTestDlg::OnBnClickedCancel2()      
  {              
    Son son(1,2);          
    son.Display();          
    son.DisplayV();          
    ((Father)son).Display();   //输出:Father::Display  var1 = 1
    ((Father)son).DisplayV(); //输出:Father::DisplayV  var1 = 1
                 
    //指针运行情况          
    TRACE("----------------------------------/n");  
    Son *lpFson = &son;        
    lpFson->Display();        
    lpFson->DisplayV();        
    ((Father*)lpFson)->Display();//输出:Father::Display  var1 = 1
    ((Father*)lpFson)->DisplayV();//输出:Son::Display  var1 = 1
  }              

        
二. 非多态及多态实现       
1.简单理解上
编译器根据元素a和b很容易判断出来是静态编译,然后根据此时调用函数的定义类型就可以决定调用哪个函数,(进行过强制转换,就是转换后的的类的函数,未转换过的话,就是定义时候的类型)
而如果是动态编译,将根据指向的实在对象里记录的虚拟表指针,在动态运行时查虚拟表确定函数。
2.从编译器生成的汇编代码的验证:
请参考:
浅析C++中虚函数的调用及对象的内部布局
http://blog.csdn.net/starlee/archive/2008/02/13/2089358.aspx
里面有详细的汇编代码说明

 

<注>作为例外,类的构造函数在符合a,b条件时不进行动态联编,因为这个时候子类的对象还没有创建好,无法实现动态联编。