由剑指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的异常。这样我们的调试程序就能够接收到这个异常,然后进行相应的处理。具体.......所以,我们写的是异常代码,实际工作中空类型又有什么意义呢?具体会设计编译器逆向工程,有机会可以看看。
回到我们标题:
二、虚函数的问题
- 当类中声明虚函数时,编译器会在类中生成一个虚函数表,若子类自己有虚函数也会生成一个虚函数表;
- 虚函数表是一个存储 类的virtual成员函数指针的数据结构;
- 当存在虚函数时,每个对象都有一个指向虚函数表的指针(类本身没有指针,有一个编译器维护的虚函数表。对象有虚函数指针,我们sizeof是测的是对象所占内存);
- 若一个对象继承于两个都有虚函数的父类,他要维护两个虚函数指针;
- 虚函数指针存在于对象实例中最前面的位置;
#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引发的思考——对象中虚函数指针的大小的主要内容,如果未能解决你的问题,请参考以下文章