C++多态详谈

多态

1.静态多态

静态多态(早绑定):编译器在编译期间完成的,编译器根据函数实参的类型(可能会出现隐式类型转换),可推断出要调用的那个函数,如果有对应的函数就调用该函数,否则出现编译错误。

2.动态多态

动态多态(晚绑定):在程序执行期间(非编译期间)判断所引用对象的实际类型,根据其实际类型调相应的方法。 使用virtual关键字修饰类的成员函数时,指明该函数为虚函数,派生类需要重新实现。编译器将实现动态绑定。

动态绑定条件

  1. 必须是虚函数。
  2. 通过基类类的引用或者指针调用虚函数。

    注意

    1. 这里是列表文本构造函数不能用virtual修饰。(对象还未构造,如何查虚表)
    2. static和virtual不能一起用。(静态函数没有this指针)
    3. 赋值运算符重载可以用virtual修饰,但不建议定义为虚函数。(在派生类和基类的赋值时可能会有问题)
    4. 友元函数不能用virtual修饰。(友元不能传递)
    5. 析构函数可以用virtual修饰。(当类内有虚函数时,最好把析构函数给成虚函数。或者派生类中有申请空间并释放空间的动作时,也给成虚函数)

纯虚函数

在成员函数的形参后面写上=0,则成员函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。纯虚函数在派生类中重新定义以后,派生类才能实现实例化出对象。

总结

  1. 派生类重写基类的虚函数实现多态,要求函数名、参数列表、返回值完全相同(协变除外)
  2. 基类中定义了虚函数,在派生类中该函数始终保持虚函数的特性。
  3. 只有类的非静态成员函数才能定义为虚函数,静态成员函数不能定义为虚函数。
  4. 如果在类外定义虚函数,只能在声明函数时加virtual关键字,定义时不用加。
  5. 构造函数不能定义为虚函数,虽然可以将赋值运算符重载函数定义为虚函数,但最好不要这么做,使用时容易混淆。
  6. 不要在构造函数和析构函数中调用虚函数,在构造函数和析构函数中,对象是不完整的,可能会出现未定义的行为。
  7. 最好将基类的析构函数声明为虚函数。(析构函数比较特殊,因为派生类的析构函数跟基类的析构函数名称不一样,但是构成覆盖,这里编译器做了特殊处理)
  8. 虚表是所有类对象实例公有的。

虚表剖析

  1. 对于有虚函数的类,编译器都会维护一张虚表,对象的前四个字节就是指向虚表的指针。 输入图片说明
  2. 没有覆盖 输入图片说明
class CBase 
{
public:          
    CBase(){m_iTest = 10;}           
    virtual void FunTest0(){cout<<"CBase::FunTest0()";}           
    virtual void FunTest1(){cout<<"CBase::FunTest1()";}           
    virtual void FunTest2(){cout<<"CBase::FunTest2()";} 
private:          
    int m_iTest;
};
class CDerived:public CBase {
public:           
    virtual void FunTest4(){cout<<"CDerived::FunTest4()";}           
    virtual void FunTest5(){cout<<"CDerived::FunTest5()";}           
    virtual void FunTest6(){cout<<"CDerived::FunTest6()";}
};
typedef void (*FUN_TEST)();
void FunTest() 
{          
    CBase base; 
    cout<<"CBase vfptr:"<<endl;
    for (int iIdx = 0; iIdx < 3; ++iIdx)     
    {                   
        FUN_TEST funTest = (FUN_TEST)(*((int*)*(int *)&base + iIdx));
        funTest();              
        cout<< ": "<<(int *)funTest<<endl;      
    }
    cout<<endl;
    CDerived derived;      
    cout<< "CDerived vfptr:"<<endl;        
    for (int iIdx = 0; iIdx < 6; ++iIdx)        
    {                
        FUN_TEST funTest = (FUN_TEST)(*((int*)*(int *)&derived + iIdx));      
        funTest();                  
        cout<< ": "<<(int *)funTest<<endl;      
    } 
}
int main()
{

    FunTest();
    return 0;
}

基类中的构造函数:填写虚表 派生类:

  1. 先拷贝基类虚表。
  2. 若有重写,替换(覆盖)相同位置的函数。
  3. 后面添加自己特有的虚函数。 虚函数调用:先找到虚表,从表中找到调用的函数。

    有覆盖

    class CBase { 
    public:         
     virtual void FunTest0(){cout<<"CBase::FunTest0()"<<endl;}   
     virtual void FunTest1(){cout<<"CBase::FunTest1()"<<endl;}          
     virtual void FunTest2(){cout<<"CBase::FunTest2()"<<endl;}         
     virtual void FunTest3(){cout<<"CBase::FunTest3()"<<endl;}
    };
    class CDerived:public CBase
    {
    public:    
     virtual void FunTest0(){cout<<"CDerived::FunTest0()" <<endl;}     
     virtual void FunTest1(){cout<<"CDerived::FunTest1()" <<endl;} 
     virtual void FunTest4(){cout<<"CDerived::FunTest4()" <<endl;} 
     virtual void FunTest5(){cout<<"CDerived::FunTest5()" <<endl;}
    };
    typedef void (*_pFunTest)();
    void FunTest()
    {    
     CBase base;           
     for (int iIdx = 0; iIdx < 4; ++iIdx)       
     {                
         _pFunTest  pFunTest = (_pFunTest)(*(( int*)*(int *)&base + iIdx));         
         pFunTest();
     }
     cout<<endl;     
     CDerived derived;     
     for (int iIdx = 0; iIdx < 6; ++iIdx)      
     {                   
         _pFunTest  pFunTest = (_pFunTest)(*((int*)*(int *)&derived + iIdx));   
         pFunTest();         
     } 
    }
    void TestVirtual()
    {     
     CBase base0;    
     CDerived derived;  
     CBase& base1 = derived; 
    }
    int main() 
    {     
     FunTest();  
     TestVirtual();     
     return 0; 
    }
    

    输入图片说明 输入图片说明

    多重继承:没有虚函数覆盖

    class CBase0 {
    public:       
     CBase0(){m_iTest = 0xB0;}  
     virtual void PrintB0(){cout<<"m_iTest = "<<hex<<m_iTest<< "  CBase0::PrintB0()" <<endl;}      
     int m_iTest; 
    };
    class CBase1 {
    public:          
     CBase1(){m_iTest = 0xB1;}    
     virtual void PrintB1(){cout<<"m_iTest = "<<hex<<m_iTest<< "  CBase1::PrintB1()" <<endl;}   
     int m_iTest; 
    };
    class CBase2 { 
    public:
     CBase2(){m_iTest = 0xB2;}        
     virtual void PrintB2(){cout<<"m_iTest = "<<hex<<m_iTest<< "  CBase2::PrintB2()" <<endl;}    
     int m_iTest;
    };
    class CDerived:public CBase0, public CBase1, public CBase2 
    { 
    public:      
     CDerived(){m_iTest = 0xD0;}     
     virtual void PrintD(){cout<<"m_iTest = "<<hex<<m_iTest<< "  CDerived::PrintD()"<<endl;}      
     int m_iTest; 
    };
    typedef void (*VFTABLE_FUN)();
    void PrintVfPTab(char * _pStr, int *_pVfAddr) 
    {
     cout<<_pStr<<endl;        
     for (int iIdx = 0; ;iIdx++)        
     {                 
         VFTABLE_FUN pPrintVTab = (VFTABLE_FUN)(*(_pVfAddr + iIdx));           
         if (NULL == pPrintVTab)             
         {                             
             break;             
         }                
         pPrintVTab();          
         cout<<( int*)pPrintVTab<<endl;     
     }        
     cout<<endl;
    }
    void FunTest() {    
     CDerived derived;      
     cout<< sizeof(derived)<<endl;
     int *pVfTAddr = NULL;       
     CBase0& base0 = derived;      
     pVfTAddr = ( int*)(*(int *)&base0);    
     PrintVfPTab( "CBase0 virtual Tab:", pVfTAddr);
     CBase1& base1 = derived;    
     pVfTAddr = ( int*)(*(int *)&base1);    
     PrintVfPTab( "CBase1 virtual Tab:", pVfTAddr);
     CBase2& base2 = derived;    
     pVfTAddr = ( int*)(*(int *)&base2);   
     PrintVfPTab( "CBase2 virtual Tab:", pVfTAddr);
     pVfTAddr = ( int*)(*(int *)&derived);          
     PrintVfPTab( "CDerived virtual Tab:" , pVfTAddr);
     derived.PrintB0();      
     derived.PrintB1();   
     derived.PrintB2();      
     derived.PrintD();
    }
    

    输入图片说明 输入图片说明

    多重继承:有虚函数覆盖

    class CBase0 {
    public:        
     CBase0(){m_iTest = 0xA0;}     
     virtual void Print(){cout<<"m_iTest = "<<hex<<m_iTest<< "  CBase2::Print()"<<endl;}     
     int m_iTest;
    };
    class CBase1 {
    public:        
     CBase1(){m_iTest = 0xB0;}   
     virtual void Print(){cout<<"m_iTest = "<<hex<<m_iTest<< "  CBase2::Print()"<<endl;}     
     int m_iTest;
    };
    class CBase2
    {
    public:   
     CBase2(){m_iTest = 0xC0;}   
     virtual void Print(){cout<<"m_iTest = "<<hex<<m_iTest<< "  CBase2::Print()"<<endl;}     
     int m_iTest; 
    };
    class CDerived:public CBase0, public CBase1, public CBase2 
    { 
    public:        
     CDerived(){m_iTest = 0xD0;}         
     virtual void Print(){cout<<"m_iTest = "<<hex<<m_iTest<< "  CDerived::Print()"<<endl;}     
     int m_iTest;
    };
    void FunTest() {  
     CDerived derived;        
     cout<< sizeof(derived)<<endl;      
     CBase0& base0 = derived;       
     base0.Print();         
     CBase1& base1 = derived;      
     base1.Print();     
     CBase2& base2 = derived;    
     base2.Print();        
     derived.Print(); 
    }
    

    输入图片说明 若有多个虚表,则自己的虚拟函数加在第一个虚表的末尾。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值