多态以及它的单继承、多继承、菱形继承的对象模型

什么是多态

同一件事物在不同的场景下表现忽的多种形态。不同类的对象对同一消息做出响应,同一消息可以根据发送对象的不同而采用多种不同的行为方式。

静态多态
在编译期间,确定程序的行为(确定具体调用哪个函数)
动态多态
程序运行期间,才能确定程序的运行行为
通常,虚函数是动态绑定,非虚函数是静态绑定,缺省参数值也是静态绑定

  • 实现动态多态的条件:
  1.基类中必须包含虚函数,在派生类中必须对基类中的虚函数进行重写。
       ------例外:
    a>析构函数(函数名不同)
    b>协变:基类虚函数返回基类的引用/指针
            派生类函数返回派生类的引用/指针
   2.通过基类的指针/引用去调用虚函数
  • 那什么是重写呢?

    前提是:在继承体系中
    1.把基类中的某虚函数再在派生类中定义一个一样的虚函数,这个虚函数必须与基类中虚函数原型保持一致。
    2.派生类中函数virtual可以不写,仍旧保持虚函数特性
    3.基类与派生类虚函数的访问权限可以不同,但是基类中虚函数必须是公有的。

  • 什么是同名隐藏

    继承体系中,基类与派生类中具有相同名称的成员(成员变量或成员函数),当通过派生类对象调用相同名称的成员时,优先调用派生类。

这里写图片描述
注意:
virtual和static一样只能和函数的声明放在一块。
virtual不能和static放在一块。

多态的原理:

虚函数表:通过一块连续内存来存储虚函数的地址。按照虚函数在基类中的声明次序添加到虚函数表中。

//构造函数里插了一条语句,把虚表的地址放到类的前四个字节,如果没有给构造函数,自动合成一个。
//如果给了,什么也没有做,那编译器就把它改写了。
class Base
{
public:
   Base()
   {}
  virtual void FunTest1()
   {
     cout << "Base::FunTest1()" << endl;
   }
  int _b;
};
int main()
{
   Base b;
   return 0;
}

不同继承下,虚函数的对象模型

  • 1.单继承

    定义这样的基类和派生类:

class Base
{
public:
       Base()
       {}

       virtual void FunTest1()
       {
              cout << "Base::FunTest1()" << endl;
       }
       virtual void FunTest2()
       {
              cout << "Base::FunTest2()" << endl;
       }
       virtual void FunTest3()//加或者删了虚函数,记得重新生成解决方案
       {
              cout << "Base::FunTest3()" << endl;
       }
       int _b;
};
class Derived : public Base
{
public:
       virtual void FunTest5()
       {
              cout << "Derived::FunTest5()" << endl;
       }
       virtual void FunTest2()
       {
              cout << "Derived::FunTest2()" << endl;
       }
       virtual void FunTest4()
       {
              cout << "Derived::FunTest4()" << endl;
       }
       int _d;
};

把虚表打印出来:


typedef void(*PVTF)();//PVTF是void(*)()的别名
void PrintfVTF(Base& b,const string& str)//传基类引用的好处:传派生类和基类的对象都可以打印(赋值兼容规则)
{
       cout << str << endl;
       //先找到对象前四个字节,转换后在解引用,但是解引用后只是一个整数
       PVTF* pVTF = (PVTF*)*(int*)&b;
       while (*pVTF)
       {
              (*pVTF)();//调这个函数
              pVTF++;
       }
       cout << endl;
}
int main()
{
       Base b;
       PrintfVTF(b,"Base VTF:");
       Derived d1;
       Derived d2;
       PrintfVTF(d1, "Derived VTF:");
       return 0;
}

这里写图片描述

一个类的多个对象共享同一张虚表:
这里写图片描述
对于重写的虚函数和没有重写的虚函数,是如何来调的:
这里写图片描述

  • 2.多继承
    首先,我们定义这样的基类和派生类:
    这里写图片描述

这里写图片描述
对于单继承来说,在派生类里没有重写的函数是在基类的虚表后面,那对于多继承,派生类里没有重写的函数是在哪个基类的虚表后面呢?
那我们把两个基类的虚函数表打印出来看看:

这里写图片描述
3.菱形继承
同样,我们声明这样的类:
这里写图片描述

对于菱形继承存在二义性问题,那我们把这个多态里的菱形继承让它虚拟继承,来看看对象模型:
这里写图片描述

现在我们来看,这连个地址哪个是虚表地址,哪个是偏移量表格地址:
这里写图片描述

所以,多态中的虚拟继承里,第一个地址是虚表地址,第二个地址是虚拟继承里偏移量表格的地址。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值