有图有真相带你理解C++中虚函数调用机制

高手请绕道,本文给初学者,本来想好好排版一下,实在被CSDN这编辑器搞的没脾气了,排版可能有点乱

高手请绕道,本文献给初学者。

一、虚函数

        在面向对象的高级语言中,实现多态机制,必不可少的重要一部分就是虚函数。在C++中,当一个类中含有virtual修饰的成员函数时,这个类和普通的类将有一些细微差别。我们先来看一个简单的例子.

/*初学者可以忽视这行(排除字节对齐引起的测试干扰)*/

#pragma pack(1)//开始

class NormalClass{

    int data;

    void fun(){}

};

class VirtualClass{

    int data;

public:

    virtual void fun(){}

};

#pragma pack()//结束

 

void TestPolymorphsim6(void)

{

    cout << "sizeof(NormalClass)" << sizeof(NormalClass) << endl;

    cout << "sizeof(VirtualClass)" << sizeof(VirtualClass) << endl;

}

 

输出结果下:

sizeof(NormalClass)4

sizeof(VirtualClass)8

通过上面的测试,我们发现含有虚函数的类,其大小比普通类多了4个字节。那么这4个字节到底存放的是什么呢?答案是这4个字节存放的是一个指针,这个指针指向一张虚函数表。

二、虚函数表

       C++中,当类中包含虚函数成员时,编译器会为这个类生成一张虚函数表。这个表可以理解成一个数组,数组的每个元素是每个虚函数的入口地址。而我们的类每次实例化一个对象时,系统都会为这个对象多分配4个字节,这4个字节存放的就是指向虚函数表的指针。我们来看个简单的例子:

       

/*初学者可以忽视这行(排除字节对齐引起的测试干扰)*/

#pragma pack(1)//开始

class VirtualClass{

int data;

public:

VirtualClass():data(0x11223344){

}

virtual void fun1(){}

virtual void fun2(){}

};

#pragma pack()//结束

 

void TestPolymorphsim6(void)

{

VirtualClass obj;

cout << sizeof(obj) << endl;

}

程序输出如下:

8

我们可以看到,当我们定义了多个虚函数后,我们的对象大小仍旧是8,所以不管虚函数有多少个,它都不会影响对象的大小,在对象的内存里面只存储一个虚表指针,虚函数的个数只会影响虚表的大小。


好的,上面的截图展示了含有虚函数的类,实例化对象后,对象的内存模型,我们也确实看到了4个字节的虚表指针,我们也看到了这张虚表,它涵有2个元素。分别是fun1fun2.下面我们要来看下当使用这个类来派生一个子类时的情况。毕竟多态是建立在继承的继承上,没有继承谈不上多态。

#pragma pack(1)//开始

class VirtualClass{

int data;

public:

VirtualClass():data(0x11223344){

}

virtual void fun1(){}

virtual void fun2(){}

};

 

class SubClass : public VirtualClass{//子类为空

};

#pragma pack()//结束

 

void TestPolymorphsim6(void)

{

VirtualClass obj1;

SubClass obj2;

cout << sizeof(obj2) << endl;

}

我们定义了一个子类,这个子类继承VirtualClass,但是没有重写父类的虚函数,我们观察一下两个对象的内存




现在我们来重写父类的虚函数,fun1(), 因为多态一个重要的条件就是重写。不重写就没什么意思了,重写后的代码如下:

class VirtualClass{

int data;

public:

VirtualClass():data(0x11223344){

}

virtual void fun1(){}

virtual void fun2(){}

};

 

class SubClass : public VirtualClass{//子类为空

void fun1(){}

};

#pragma pack()//结束

 

void TestPolymorphsim6(void)

{

VirtualClass obj1;

SubClass obj2;

cout << sizeof(obj2) << endl;

}


我们再来看下两个对象的内存模型。



上图蓝色框中有两个错别字 不是已经存放父类的fun2, 是依旧存放父类的fun2,


最后我们来总结一下:通过父类指针或者引用来调用虚函数时,多态是怎么实现的:


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值