多态的实现原理

一、多态的概念      


       多态就是多种形态,C++的多态分为静态多态与动态多态。

静态多态就是重载,因为在编译期决议确定,所以称为静态多态。在编译时就可以确定函数地址。

动态多态就是通过继承重写基类的虚函数实现的多态,因为是在运行时决议确定,所以称为动态多态。运行时在虚函数表中寻找调用函数的地址。

       在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数。

如果对象类型是子类,就调用子类的函数;如果对象类型是父类,就调用父类的函数,(即指向父类调父类,指向子类调子类)此为多态的表现。

例:


二、多态的实现原理


一个接口,多种方法

1. 用virtual关键字声明的函数叫做虚函数,虚函数肯定是类的成员函数。

2. 存在虚函数的类都有一个虚函数表叫做虚表。当类中声明虚函数时,编译器会在类中生成一个虚函数表。

3. 类的对象有一个指向虚表开始的虚指针。虚表是和类对应的,虚表指针是和对象对应的。

4. 虚函数表是一个存储类成员函数指针的数据结构。

5. 虚函数表是由编译器自动生成与维护的。

6. virtual成员函数会被编译器放入虚函数表中。

7. 当存在虚函数时,每个对象中都有一个指向虚函数的指针(C++编译器给父类对象,子类对象提前布局vptr指针),当进行test(parent *base)函数的时候,C++编译器不需要区分子类或者父类对象,只需要在base指针中,找到vptr指针即可)。

8. vptr一般作为类对象的第一个成员。

三、探索虚表


       虚表是通过一块连续的内存来存储虚函数的地址。这张表解决了继承、虚函数(重写)的问题。在有虚函数的对象实例中都存在这样虚表指针,指向虚函数表,虚表它就像一张地图,指向了实际调用的虚函数。

例:

class Person   
{   
public :       
    virtual void BuyTickets()      
    {           
    cout<<" 买票"<< endl;     
    }  
protected :      
    string _name ;   // 姓名   
};  
  
class Student : public Person   
{   
public :  
     virtual void BuyTickets()  
     {           
     cout<<" 买票-半价 "<<endl ;  
     }  
protected :  
     int _num ;   //学号  
};  
  
//void Fun(Person* p)   
void Fun (Person& p)  
{  
     p.BuyTickets ();  
}  
  
void Test ()  
 {  
     Person p ;  
     Student s ;  
     Fun(p );  
     Fun(s );  
}  

可以看到派生类Derive::func1重写基类Base::func1,覆盖了相应虚表位置上的函数。 ps:可以看到这里没有看到派生类Derive中的func3和func4,这两个函数就放在func2的后面,这里没有显示是VS的问题(bug)。

每个类都有一个虚函数表,每个对象都有一个虚函数表指针。如果对象类型是子类,就调用子类的函数;如果对象类型是父类,就调用父类的函数,(即指向父类调父类,指向子类调子类)此为多态的表现。

多继承对象模型
 

class Base1
 {
 public :
     virtual void func1()
    {
          cout<<"Base1::func1" <<endl;
    }
     virtual void func2()
    { 
         cout<<"Base1::func2" <<endl;
    }
private :
     int b1 ;
 };
class Base2
 { 
public :
     virtual void func1()
    {
          cout<<"Base2::func1" <<endl;
    }
     virtual void func2()
    {
         cout<<"Base2::func2" <<endl;
   }
private :
     int b2 ;
 };
class Derive : public Base1, public Base2
 { 
public :
virtual void func1()
    { 
         cout<<"Derive::func1" <<endl;
    }
     virtual void func3()
   { 
         cout<<"Derive::func3" <<endl;
    }
private :
     int d1 ; 
};
 
typedef void (* FUNC) ();
void PrintVTable (int* VTable)
 {
     cout<<" 虚表地址>"<< VTable<<endl ;
 
     for (int i = 0; VTable[i ] != 0; ++i)
    { 
         printf(" 第%d个虚函数地址 :0X%x,->", i , VTable[i ]); 
         FUNC f = (FUNC) VTable[i ];
          f();
    }
    cout<<endl ;
 }
 
void Test1 ()
 { 
    Derive d1 ;
    int* VTable = (int*)(*( int*)&d1 );
    PrintVTable(VTable );
     // Base2虚函数表在对象Base1后面    
    VTable = (int *)(*((int*)&d1 + sizeof (Base1)/4));
     PrintVTable(VTable );
 }

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值