由剑指offer引发的思考——对象中虚函数指针的大小

Posted FireLightning

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了由剑指offer引发的思考——对象中虚函数指针的大小相关的知识,希望对你有一定的参考价值。

先看一个简单的问题:

一、定义一个空的类型,对于其对象我们sizeof其大小,是1字节。因为我们定义一个类型,编译器必须为其分配空间,具体分配多少是编译器决定,vs是1字节,分配在栈区。

      那,这一个字节会被初始化吗?

举个例子:

#include<iostream>
class parent{

};
int main() {
    parent my_par;
    int a = 0;
    std::cout << sizeof(my_par) << " "<< &my_par<<" "<<&a;
    while (1);
    return 0;
}

其输出:

   

我们查看内存,看得到都初始化为:CC CC CC CC                                       

多调试几次,每次地址都不一样,但是内存值都是CC。为什么都是CC?

 因为0xCC对应的是INT3断点,简单地说就是将要断下的指令地址处的第一个字节设置为0xCC,软件执行到0xCC(对应汇编指令INT3)时,会触发异常代码为EXCEPTION_BREAKPOINT的异常。这样我们的调试程序就能够接收到这个异常,然后进行相应的处理。具体.......所以,我们写的是异常代码,实际工作中空类型又有什么意义呢?具体会设计编译器逆向工程,有机会可以看看。

回到我们标题:

二、虚函数的问题

  1. 当类中声明虚函数时,编译器会在类中生成一个虚函数表,若子类自己有虚函数也会生成一个虚函数表
  2. 虚函数表是一个存储 类的virtual成员函数指针的数据结构;
  3. 当存在虚函数时,每个对象都有一个指向虚函数表的指针(类本身没有指针,有一个编译器维护的虚函数表。对象有虚函数指针,我们sizeof是测的是对象所占内存)
  4. 若一个对象继承于两个都有虚函数的父类,他要维护两个虚函数指针;
  5. 虚函数指针存在于对象实例中最前面的位置;
#include <iostream>
using namespace std;
 
class Base1{
    virtual void fun1(){}
    virtual void fun11(){}
public:
    virtual ~Base1();
};
 
class Base2{
    virtual void fun2(){}
};
 
class DerivedFromOne: public Base2
{
    virtual void fun2(){}
    virtual void fun22(){}
};
 
class DerivedFromTwo: public Base1, public Base2      //多继承
{
    virtual void fun3(){}
};
 
void main()
{
Base2 my_base; cout
<< "sizeof Base1 " << sizeof(Base1) << "输出vptb"<<(int*)*(int*)(&my_base)<< endl;//太复杂了,以后再说。 cout << "sizeof Base2" << sizeof(Base2) << endl; cout << "sizeof FromOne " << sizeof(DerivedFromOne) << endl; cout << "sizeof FromTwo " << sizeof(DerivedFromTwo) << endl; system("pause") }

 输出:

 

 

可以看出:

1、每个单继承,对象都有一个vptr指针;多继承,对象就会有多个虚函数指针指向父类的虚函数表。虚函数表不占栈内存,由编译器维护,在代码区?我们测的是对象的内存

2、Base2的对象的地址,就是vptr指向的虚函数表的地址,可以打开内存查看。这意味着我们通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。

    

以上是关于由剑指offer引发的思考——对象中虚函数指针的大小的主要内容,如果未能解决你的问题,请参考以下文章

剑指offer速记

剑指Offer:矩形覆盖N1

C++中虚函数实现原理揭秘

算法热门:链表中倒数第k个结点(剑指Offer 22)

剑指OFFER 反转链表

剑指Offer(Java版)第十六题:给定单向链表的头指针和一个结点指针,定义一个函数在O时间删除该结点。