zoukankan      html  css  js  c++  java
  • 虚函数 动态绑定 实现方式是:虚函数表

    定义为 virtual的 函数是基类期待派生类(derived class)重定义的, 基类期待 派生类继承的函数不能定义为虚函数。

    动态绑定:程序能 使用继承层次中任意类型的对象,无需关心对象的具体类型。  使用这些类的程序无须区分函数在基类还是在继承类中定义的。

    尽管不是必须,派生类一般会重定义基类中的虚函数。如果没有重定义,则继承基类中定义的版本。派生类中虚函数的声明原型必须与基类中的定义方式完全匹配,但有一个例外:基类中返回值是对基类性的引用(或指针)的虚函数,在派生类中虚函数可以返回 基类和派生类 的引用(或指针)。

    C++默认不适用动态绑定,要触发动态绑定,得满足2条件:1,只有指定为虚函数的成员函数才能进行动态绑定,2,必须通过基类类型的引用(或指针)才能进行函数调用

    可以使用基类类型的引用(或指针)来引用基类类型或派生类类型对象。基类类型引用和指针的关键点在于:静态类型(编译时可知的引用类型或指针类型)与动态类型(运行时才可知的,指针或引用绑定的对象类型)可能不同

    即动态绑定是由实参决定的。 

     1 class Cshape
     2 {
     3 public:
     4     void SetColor(intcolor){m_nColor=color;}
     5     virtual void Display(void){cout<<"Cshape"<<endl;}
     6 private:
     7     int m_nColor;
     8 };
     9  
    10 class Crectangle:public Cshape{
    11 public:
    12     virtual void Display(void)    
    13     {
    14         cout<<"Crectangle"<<endl;
    15     }
    16 };
    17  
    18 class Ctriangle:public Cshape{
    19                 virtual void Display(void)
    20                 {cout<<"Ctriangle"<<endl;}
    21 };
    22  
    23 class Cellipse:public Cshape{
    24 public:
    25     virtual void Display(void)
    26     {cout<<"Cellipse"<<endl;}
    27 };
    28  
    29 voidmain()
    30 {
    31     Cshape obShape;
    32     Cellipse obEllipse;
    33     Ctriangle obTriangle;
    34     Crectangle obRectangle;
    35     Cshape*pShape[4]={&obShape,&obEllipse,&obTriangle,&obRectangle};
    36     for(int i=0;i<4;i++)
    37         pShape[i]->Display();//动态绑定
    38 }

    本程序运行结果:

    Cshape
    Cellipse
    Ctriangle
    Crectangle
    如果把Cshape类里面virtual void Display(void) 中的virtual去掉的话
    运行结果就不一样了:
    Cshape
    Cshape
    Cshape
    Cshape
     

    c++规定,当一个成员函数被声明为虚函数后,其派生类中的同名函数都自动成为虚函数。因此,在子类从新声明该虚函数时,可以加,也可以不加,但习惯上每一层声明函数时都加virtual,使程序更加清晰。

    继承层次的根类 一般要定义虚析构函数
     
    找工作时,面试官经常会问虚函数是如何实现的,我就懵了,以为知道以上理论虚函数就过关了,书上也没说过虚函数是如何实现的,现在在此补一补:
     
    C++中的虚函数的实现一般是通过虚函数表(C++规范并没有规定具体用哪种方法,但大部分的编译器厂商都选择此方法)。
    类的虚函数表是一块连续的内存,每个内存单元中记录一个JMP指令的地址。
    注意的是,编译器会为每个有虚函数的类创建一个虚函数表,该虚函数表将被该类的所有对象共享。类的每个虚成员占据虚函数表中的一行。如果类中有N个虚函数,那么其虚函数表将有N*4字节的大小。
    虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。简称为V-Table。在这个表中,主要是一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其真实反应实际的函数。这样,在有虚函数的类的实例中分配了指向这个表的指针的内存,所以,当用父类的指针来操作一个子类的时候,这张虚函数表就显得尤为重要了,它就像一个地图一样,指明了实际所应该调用的函数。
    编译器应该是保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证取到虚函数表的有最高的性能——如果有多层继承或是多重继承的情况下)。 这意味着可以通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。

    虚基类与虚函数没有关系。用于多重继承,防止二义性产生。

  • 相关阅读:
    WebSocket使用及优化(心跳机制与断线重连)
    JS案例:触底懒加载
    你知道近来年大火的DDD是如何兴起的吗?以及与微服务的关系
    Sql Server的Cross Apply用法
    跨域信息传递解决方案
    【转】理解字节序
    NATAPP优惠码
    <学习笔记>筛法
    <学习笔记>线性基
    【react + BizCharts】
  • 原文地址:https://www.cnblogs.com/xaf-dfg/p/3781350.html
Copyright © 2011-2022 走看看