从内存模型角度来看C++多态实现机制

C++中的多态实现通过虚函数表来完成。每个对象包含一个指向虚函数表的指针,即使对象类型转换,其内部虚表指针保持不变,从而实现动态绑定。虚表在继承中保持并可能覆盖基类虚函数,且派生类虚函数地址顺序与基类一致。
最近对多态的实现机制比较感兴趣,本文从C++内存模型角度去看待多态的实现机制。实例如下:

#include<iostream>
#include <string>
#include <stdio.h>
using namespace std;

class Animal
{
    public:
           Animal(int a = 1, int b = 2)
           {
               this->a = a;
               this->b = b;
           }

          virtual   void sleep(int minutes = 30)
            {
                cout<<"animal sleep "<<minutes<<" minutes"<<endl;
            }
          virtual  void breathe()
          {
              cout<<"animal breathe"<<endl;
          }
          virtual void eat()
          {
              cout<<"animal eat"<<endl;
          }
          private:
            int a;
            int b;
};


class Fish:public Animal
{
  public:

        Fish(int c) {
             this->c = c;
        }

         virtual void breathe()
            {
                cout<<"fish breathe"<<endl;
            }

         virtual void sleep(int minutes = 50)
            {
                cout<<"fish sleep "<<minutes<<" minutes"<<endl;
            }
          virtual void swim(void) //Fish子类特有函数
          {
              cout<<"fish swim"<<endl;
          }
            private:
            int c;
};



int main()
{
 typedef void (*Fun1)(void); //指向breathe()函数
 Fun1 pFun1 = NULL;
 typedef void (*Fun2)(int); //指向sleep()函数
 Fun2 pFun2 = NULL;

 cout<<"<<<<<<Animal 对象打印信息>>>>>>"<<endl;
 Animal an(1,2);
cout<<"Animal类虚函数表地址:"<<(int*)*(int*)&an<<endl;
 cout<<"对象an内存模型中成员变量a =  "<<* ((int*)&an+1)<<endl;
 cout<<"对象an内存模型中成员变量b =  "<<* ((int*)&an+2)<<endl;
pFun1 =  (Fun1)*(int*)*(int*)&an;
 pFun1();
pFun2 =  (Fun2)*((int*)*(int*)&an+1);
pFun2(50);
pFun1 =  (Fun1)*((int*)*(int*)&an+2);
pFun1();
cout<<"<<<<<<Fish 对象打印信息>>>>>>"<<endl;
 Fish fh(5);

 cout<<"Fish类虚函数表地址:" <<(int*)*(int*)&fh<<endl;
 cout<<"对象fh内存模型中成员变量a =  "<<* ((int*)&fh+1)<<endl;
 cout<<"对象fh内存模型中成员变量b =  "<<* ((int*)&fh+2)<<endl;
 cout<<"对象fh内存模型中成员变量c =  "<<* ((int*)&fh+3)<<endl;
pFun1 =  (Fun1)*(int*)*(int*)&fh;
 pFun1();
pFun2 =  (Fun2)*((int*)*(int*)&fh+1);
pFun2(50);
pFun1 =  (Fun1)*((int*)*(int*)&fh+2);
 pFun1();
pFun1 =  (Fun1)*((int*)*(int*)&fh+3);
 pFun1();
cout<<"<<<<<<Animal指针指向Fish 对象打印信息>>>>>>"<<endl;
Animal *anPtr = &fh;

cout<<"Animal类指针指向Fish类虚函数表地址:" << (int*)*(int*)anPtr<<endl;
 cout<<"Animal类指针指向Fish类所属内存区域a = "<<* ((int*)anPtr+1)<<endl;
 cout<<"Animal类指针指向Fish类所属内存区域b = "<<* ((int*)anPtr+2)<<endl;
pFun1 =  (Fun1)*(int*)*(int*)anPtr;
 pFun1();
pFun2 =  (Fun2)*((int*)*(int*)anPtr+1);
pFun2(50);
pFun1 =  (Fun1)*((int*)*(int*)anPtr+2);
 pFun1();
 pFun1 =  (Fun1)*((int*)*(int*)anPtr+3);
 pFun1();
 return 0;
}

<<<<<<Animal 对象打印信息>>>>>>
Animal类虚函数表地址:0x8049178
对象an内存模型中成员变量a =  1
对象an内存模型中成员变量b =  2

animal sleep 134514480 minutes
animal breathe
animal eat

<<<<<<Fish 对象打印信息>>>>>>
Fish类虚函数表地址:0x8049160
对象fh内存模型中成员变量a =  1
对象fh内存模型中成员变量b =  2
对象fh内存模型中成员变量c =  5

fish sleep 134514480 minutes
fish breathe
animal eat
fish swim

<<<<<<Animal指针指向Fish 对象打印信息>>>>>>
Animal类指针指向Fish类虚函数表地址:0x8049160
Animal类指针指向Fish类所属内存区域a = 1
Animal类指针指向Fish类所属内存区域b = 2

fish sleep 134514480 minutes
fish breathe
animal eat
fish swim


由此可以得到,Animal类an对象与Fish类fh对象的内存布局如下
 

如果一个类声明了虚函数,编译器就会在其声明的对象中的开始部分增加一个指向虚函数表指针vfptr。此实例中 Animal* anPtr 声明了一个指向Animal对象的指针,当Animal没有声明虚函数时,anPtr所指向的内容并不包括vfptr,当用Fish对象给它赋值时,只将 Fish中的Animal对象复制给anPtr所指向的Animal对象。因此anPtr->breathe()会调用Animal中的breathe()函数。

如果Animal中的breathe()是虚函数,那么当Animal *anPtr = &fh(或者Animal *anPtr = new Fish(5);)执行时,就会把Fish对象的vfptr 和Fish 对象中包含的Animal对象一同复制给anPtr所指向的对象。此后,anPtr所指向的vfptr 是Fish对象的vfptr,因此再调用anPtr->breathe()时,就会调用Fish类中的breathe()函数。

总结:对于虚函数调用来说,每一个对象内部都有一个虚表指针,该虚表指针被初始化为本类的虚表。在程序中,不管你的对象类型如何转换,该对象内部的虚表指针都是固定的,所以,才能实现动态的对象函数调用,这就是C++多态性实现的原理。此外,我们还可以看到,对于访问限定符仅仅作用在编译器层面上的,编译器制定了私有的成员变量不能被继承规则,然而在父类和子类的内存模型中可以看到,子类是在父类已有的内存模型空间中增加子类自有的成员变量。

其特点为:

1. 每一个类都有虚表。

2. 虚表可以继承,如果子类没有重写虚函数,那么子类虚表中仍然会有该函数的地址,只不过这个地址指向的是基类的虚函数实现。如果重写了相应的虚函数,那么虚表中的地址就会改变,指向自身的虚函数实现。

3. 派生类的虚表中虚函数地址的排列顺序和基类的虚表中虚函数地址排列顺序相同。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值