虚函数与虚表

一、虚函数:加virtual关键词的函数
  1. 虚函数作用:C++中的多态,进行动态绑定(父类指针可指向子类的对象),直到运行时才知道要具体调用哪个版本(哪个类定义)的函数;
  2. 它是通过虚表实现的,简单理解如下:
代码分析:
#include<iostream>
using namespace std;
class Base
{
public:
     virtual void f() { cout << "Base::f" << endl; }
     virtual void g() { cout << "Base::g" << endl; }
     virtual void h() { cout << "Base::h" << endl; }
};
int main()
{
     Base b;
      cout<<"b 对象的大小是: "<<sizeof(b)<<endl;
      cout<<"b 对象的起始地址是:"<<&b<<endl;
      cout<<"虚函数表地址: "<<*(int *)(&b)<<endl;
      cout<< "虚函数表 — 第一个函数地址:" << *((int*)*(int*)(&b)) << endl;
      cout << "虚函数表 — 第二个函数地址:" << *((int*)*(int*)(&b)+1) << endl;
      cout << "虚函数表 — 第三个函数地址:" << *((int*)*(int*)(&b)+2) << endl;

      cout<<"**********************************************************************"<<endl;

      Base c;
      cout<<"c 对象的起始地址是:"<<&c<<endl;
      cout<<"虚函数表地址: "<<*(int *)(&c)<<endl;
      cout<< "虚函数表 — 第一个函数地址:" << *((int*)*(int*)(&c)) << endl;
      cout << "虚函数表 — 第二个函数地址:" << *((int*)*(int*)(&c)+1) << endl;
      cout << "虚函数表 — 第三个函数地址:" << *((int*)*(int*)(&c)+2) << endl;
     //(int *)&b ->把b的地址转成(int *),*(int *)&b即基表指针,
     //(int*)*(int*)(&b)->基表指针指向的函数地址,*((int*)*(int*)(&b))所指向的函数内容

     return 0;
}
结果截屏:
虚函数与虚表
总结:虚表是属于类的,它只有一份,对象在被实例化的时候,首先前四个字节存储虚表指针,即类的虚表地址。
二、用虚函数实现多态。

代码:
class Base
{
public:
     virtual void f() { cout << "Base::f" << endl; }
     virtual void g() { cout << "Base::g" << endl; }
     virtual void h() { cout << "Base::h" << endl; }
};
class Derive:public Base
{
public:
     void f(){ cout << "Derive::f" <<endl; }
     virtual void g(){ cout << "Derive::g" <<endl; }
     virtual void h1(){ cout << "Derive::h1" <<endl; }
};
int main()
{
     typedef void(*Fun)(void);
     Derive d;
     Fun pFun = NULL;
      pFun = (Fun)*((int*)*(int*)(&d));
     pFun();
     pFun = (Fun)*((int*)*(int*)(&d)+1);
     pFun();
     pFun = (Fun)*((int*)*(int*)(&d)+2);
     pFun();
     pFun = (Fun)*((int*)*(int*)(&d)+3);
     pFun();

     Base *b = &d;
     b->f();

     return 0;
}
结果截屏:
虚函数与虚表
总结:1、一旦父类的成员函数声明virtual,其子类的函数不管有没有声明为virtual,都是虚函数;
          2、虚表顺序:先基类后派生类,如果派生类有同名的函数,则取代虚表中基类的函数位置;
          3、基类指针指向派生类对象,如果调用的函数是虚函数,则调动的是派生类的方法。
三、纯虚函数

class Base//抽象类
{
public :
     virtual void fun () = 0; //纯虚函数
};

class Drive:public Base
{
public:
     void fun()
     {
           cout<<"fun"<<endl;
     }
};

int main()
{
     //Base b; 抽象类不能被实例化
     Drive d;
     Base *c = &d;
     c->fun ();
     return 0;
}