虚函数与纯虚函数以及虚函数表之间的关系

1、虚函数

        简单地说,那些被virtual关键字修饰的成员函数,就是虚函数。C++中虚函数的作用主要是实现多态机制。所谓多态就是用父类指针指向子类对象,然后通过父类指针调用实际子类的成员函数,这种技术可以让父类指针有“多种形态”。

2、纯虚函数

        在虚函数形参后面写上=0,则该函数为纯虚函数。纯虚函数没有函数体;纯虚函数只有函数的名字而不具备函数的功能,不能被调用。纯虚函数的作用是在基类中为其派生类保留一个函数的名字,以便派生类根据需要对他进行定义。如果在一个类中声明了纯虚函数,在其派生类中没有对其函数进行定义,则该虚函数在派生类中仍然为纯虚函数。

3、虚函数表

        虚函数是通过虚函数表来实现的,在这个表中,主要是一个类的虚函数的地址表,这张表解决了继承、覆盖的问题。在有虚函数的实例中这张表被分配在这个实例的内存中,所以当我们用父类指针来操作一个子类的时候,这张虚函数表就像地图一样指明实际所应该调用的函数。

eg:

class Base{
public:
    virtual void f(){cout<<"Base::f"<<endl;}
    virtual void g(){cout<<"Base::g"<<endl;}
    virtual void h(){cout<<"Base::h"<<endl;}
};

typedef void(*Fun)(void);
Base b;
Fun pFun = NULL;
cout<<"虚函数表的地址:"<<(int*)(&b)<<endl;
cout<<"虚函数表的第一个函数地址:"<<(int*)*(int*)(&b)<<endl;
pFun = (Fun)*((int*)*(int*)(&b));
pFun();

/*
实际运行经果如下:(Windows XP+VS2003,  Linux 2.6.22 + GCC 4.1.3)
虚函数表地址:0012FED4
虚函数表的第一个函数地址:0044F148
Base::f

同理可得到其他虚函数:
(Fun)*((int*)*(int*)(&b)+0);  // Base::f()
(Fun)*((int*)*(int*)(&b)+1);  // Base::g()
(Fun)*((int*)*(int*)(&b)+2);  // Base::h()
*/

(1)一般继承:假如有派生类继承基类,但是没有覆盖其虚函数

class Derive :public Base
{
public:
    virtual void f1(){cout<<"Derive::f1"<<endl;}
    virtual void g1(){cout<<"Derive::g1"<<endl;}
    virtual void h1(){cout<<"Derive::h1"<<endl;}
};

Derive d;
((Fun)*((int*)*(int*)(&d)+0))();  // Base::f()
((Fun)*((int*)*(int*)(&d)+1))();  // Base::g()
((Fun)*((int*)*(int*)(&d)+2))();  // Base::h()
((Fun)*((int*)*(int*)(&d)+3))();  // Derive::f1()
((Fun)*((int*)*(int*)(&d)+4))();  // Derive::g1()
((Fun)*((int*)*(int*)(&d)+5))();  // Derive::h1()

// 父类指针指向子类对象
Base* b = &d;
((Fun)*((int*)*(int*)b+0))(); // Base::f()
((Fun)*((int*)*(int*)b+1))(); // Base::g()
((Fun)*((int*)*(int*)b+2))(); // Base::h()
((Fun)*((int*)*(int*)b+3))(); // Derive::f1()
((Fun)*((int*)*(int*)b+4))(); // Derive::g1()
((Fun)*((int*)*(int*)b+5))(); // Derive::h1()

/*
1、虚函数按照其声明顺序放在
2、父类的虚函数在子类的虚函数前面
*/

(2)一般继承:假如派生类覆盖了父类的虚函数

class Derive :public Base
{
public:
    virtual void f(){cout<<"Derive::f"<<endl;}
    virtual void g1(){cout<<"Derive::g1"<<endl;}
    virtual void h1(){cout<<"Derive::h1"<<endl;}
}

Derive b;
Base* d = &b;
(Fun)*((int*)*(int*)d+0);  // Derive::f()
(Fun)*((int*)*(int*)d+1);  // Base::g()
(Fun)*((int*)*(int*)d+2);  // Base::h()
(Fun)*((int*)*(int*)d+3);  // Derive::g1()
(Fun)*((int*)*(int*)d+4);  // Derive::h1()

// 由上可知,被覆盖的取代原来父类虚函数的位置,没覆盖的按声明顺序

(3)多重继承:无虚函数覆盖

class Base1{
public:
    virtual void f(){cout<<"Base1::f"<<endl;}
    virtual void g(){cout<<"Base1::g"<<endl;}
    virtual void h(){cout<<"Base1::h"<<endl;}
};

class Base2{
public:
    virtual void f(){cout<<"Base2::f"<<endl;}
    virtual void g(){cout<<"Base2::g"<<endl;}
    virtual void h(){cout<<"Base2::h"<<endl;}
};

class Base3{
public:
    virtual void f(){cout<<"Base3::f"<<endl;}
    virtual void g(){cout<<"Base3::g"<<endl;}
    virtual void h(){cout<<"Base3::h"<<endl;}
};

class Derive1: public Base1,public Base2,public Base3
{
public:
    virtual void f1(){cout<<"Derive1::f1"<<endl;}
    virtual void g1(){cout<<"Derive1::g1"<<endl;}    
};

// #1 直接从子类对象查找虚函数对应的地址
Derive1 d;
((Fun)*((int*)*((int*)(&d)+0)+0))(); // Base1::f
((Fun)*((int*)*((int*)(&d)+0)+1))(); // Base1::g
((Fun)*((int*)*((int*)(&d)+0)+2))(); // Base1::h
((Fun)*((int*)*((int*)(&d)+0)+3))(); // Derive1::f1
((Fun)*((int*)*((int*)(&d)+0)+4))(); // Derive1::g1

((Fun)((int*)*((int*)(&d)+1)+0))(); // Base2::f
((Fun)*((int*)*((int*)(&d)+1)+1))(); // Base2::g
((Fun)*((int*)*((int*)(&d)+1)+2))(); // Base2::h

((Fun)*((int*)*((int*)(&d)+2)+0))(); // Base3::f
((Fun)*((int*)*((int*)(&d)+2)+1))(); // Base3::f
((Fun)*((int*)*((int*)(&d)+2)+2))(); // Base3::f

// #2 从父类指针指向子类对象查找虚函数对应的地址
Base1* b1 = &d;
((Fun)*((int*)*((int*)b1+0)+0))(); // Base1::f
((Fun)*((int*)*((int*)b1+0)+1))(); // Base1::g
((Fun)*((int*)*((int*)b1+0)+2))(); // Base1::h
((Fun)*((int*)*((int*)b1+0)+3))(); // Derive1::f1
((Fun)*((int*)*((int*)b1+0)+4))(); // Derive1::g1

Base2* b2 = &d;
((Fun)*((int*)*((int*)b2+0)+0))(); // Base2::f
((Fun)*((int*)*((int*)b2+0)+1))(); // Base2::g
((Fun)*((int*)*((int*)b2+0)+2))(); // Base2::h

Base3* b3 = &d;
((Fun)*((int*)*((int*)b3+0)+0))(); // Base3::f
((Fun)*((int*)*((int*)b3+0)+1))(); // Base3::g
((Fun)*((int*)*((int*)b3+0)+2))(); // Base3::h

/*
#1和#2都说明了子类的虚函数是紧接着第一个继承的父类,
而且多重继承的虚函数表是不一样的,
(1)对于子类对象来说,虚函数表像一个二维数组一样,
数组的第一行放置第一个继承父类的虚函数和子类本身的虚函数
数组的第二行放置第二个继承父类的虚函数
数组的第三行放置第个继承父类的虚函数
(2)对于父类来说,
Base1的虚函数表有其自身的虚函数以及子类的虚函数
Base2和Base3的虚函数表都只有其自身的虚函数
*/

(4)多重继承:有虚函数覆盖

class Derive2: public Base1,public Base2,public Base3
{
public:
    virtual void f(){cout<<"Derive2::f"<<endl;}
    virtual void g1(){cout<<"Derive2::g1"<<endl;}    
};

// #1 直接从子类对象查找虚函数对应的地址
Derive2 d;
((Fun)*((int*)*((int*)(&d)+0)+0))(); // Derive2::f

// #2 从父类指针指向子类对象查找虚函数对应的地址
Base1* b1 = &d;
((Fun)*((int*)*((int*)b1+0)+0))(); // Derive2::f


Base2* b2 = &d;
((Fun)*((int*)*((int*)b2+0)+0))(); // Derive2::f


Base3* b3 = &d;
((Fun)*((int*)*((int*)b3+0)+0))(); // Derive2::f


// 这时候每个父类的f()虚函数都会被子类的f()所覆盖,其他虚函数不变

        另外,需要注意的是,当父类指针指向子类对象时,不能访问子类特有的虚函数,只能访问子类覆盖父类的虚函数。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

czy1219

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值