Skip to content

C++多态实现原理

呢,我们此时obj这个对象的大小是多大?那我们知道你创建对象的话,就会分辨内存是不是?那我们此时分配的内存应该是多少?小伙伴们思考一下这个问题,在我们这种情况下,如果我们创建的对象分配了内存,它的大小应该是多大?来,我们来看一下编译器实际运行的结果。是多少一为什么呢?因为我们创建对象就不会分配内存,但是我们对象里面什么都没有,是不是那那肯定就按照最小的单位来分配呗?所以这个单位就是一个字节,如果我在这个对象中加一个成员呢,比如说我加一个成员叫a呢。那这个时候各位小伙伴告诉我,此时我的对象大小应该多大?是不是应该就是四个字节,一个应对性的大小嘛?那我们来看一下哦,是不是四个字节没问题吧?那在这里面的话呢,我们就需要注意了。如果我们在代码中的话呢,添加了一个函数啊,我们在这个地方来添加一个函数。啊,这个函数呢?我们把它叫做function。f这个函数好,这个函数里面的话呢,我们输出了一句话啊,比如这句话就叫hello。那我想问大家的话,此时我的对象大小有没有变化?有没有加一个加了一个函数代码在里面,我们来看一下。没有任何变化,为什么?因为代码是函数是代码嘛,代码是放在代码段的嘛,跟对象。不担任对象的大小啊,这点需要注意。好,那我接下来的时候呢,在这里面的话呢,给它添加一个成员,这个成员是一个静态成员。啊,我们知道金融静态成员在内外是不是要进行初始化?我们在内外进行初始化一下。把它变成零好,这个时候我们。各位小伙伴,此时我们对象的大小应该变成几了?哎,有有些小伙伴可能会告诉我老师,这个时候两个成员了啊,两个成员他应该变成八,我们来看一下还是四。为什么?因为你是静态成员呀,静态成员是所有对象共享的呀,所有对象共享,它怎么会占用我这个对象的大小嘞?是不是所以它不占用我们这个对象的大小啊,它是在近代区的,明白不好,所以不管加多少个static的成员都不会影响我们这个对象的大小。好,那接下来的话呢,我们再来做一个实验啊,我们在这里面的话呢,给它加一个成员加什么成员呢?我们给它加上一个虚函数的成员。好虚函数啊。啊,我这个循环中里面的话呢,输出一句话。cout.好,我说出这一句话,哎,我们刚刚说加代加函数的话,它的代码是。是不是代码段不占用对象的大小?是不是那此时的话,我们加上一个虚函数之后啊,我们来看一下对象的大小。呃,有问题啊,我们来看一下。哪个地方?它怎么说?他说,我们第13行啊。第13行啊,可能是这个命名的问题啊,加上下划线。没问题啊,刚命名不规范。诶,他现在是多大?16。怎么这么什么情况?还记得我们刚刚是多少不?我们刚开始撕呀,怎么加一个续完肉之后变成了石榴呀?这是啥情况?为什么虚函数会增加我们对象的大小?没有虚函数的时候是多大看一下。运行。14加了循环肉是多大?运行。16你看有续环数之后,这个对象一下子增加了很多是吧?那这是为什么呢?那这里面的话呢?我们要给大家介绍一个。虚函数表与指向虚函数表的一个虚函数指针,那我们先看一下我们课件当中的例子。呃,我们课件当中的话呢,给出了一个parent类,在parent类当中的话呢,有两个虚函数,一个虚函数的话呢,是function 1,第二个是function 2。然后在这个类当中呢,还有两个成员变量啊,一个是a,一个是b。那我们看一下,在我这个类当中呢,是有循环周的,是有两个循环周,而且有两个成员变量呢a和b。那我课件当中呢,给出了这个对象大小的一个分析,那我们看一下是怎么分析的呢?首先的话呢,我们对它呢,做一个编译啊,我这里面为了呢,让大家分析起来更方便的话呢。我把它编译成的是32位的程序啊,咱们默认情况下,编译器编译出来的是64位的啊,那我们可以加上一个杠m32的方式呢?可以知道,我们编译出来的就是一个32位程序,我们可以来测试一下。好,我们首先对自己的二点二层呢?来看一下,我们可以通过file的命令呢来看它,那我们会发现呢?目前显示的是叉八六杠六四是不是那接下来的时候我们对我们的程序重新编译一下?我们加上一个干m32。啊,那这个时候编译完之后的话呢?我们的程序呢?就是一个32位的程序,大家有没有发现啊?在这个过程当中呢,大家需要注意啊,就是我们在编译的时候呢,默认情况下,大家可能呢,编译的时候报错报错的话呢,它会提示一个。呃,图文件找不到,找不到的话呢,大家就把这个软件包呢重新装一下就可以了,装好之后呢,你就可以正常编译成呢,这个32位的程序。那大家会发现我在课件当中呢,还加了一个干季,这个干季的目的呢,是为了增加那个GDP的调试信息,那我们是想通过GDP的方式呢?让大家真正的去看到啊,一个对象里面具体有什么样的东西呢?我们来看一下课件当中的内容。那我们启动GDP之后呀,我这边的话呢是呃,在这边的时候呢,你看啊,我这边的话呢,是把这个对象呢给它打出来了。啊,怎么打的呢?这个打的方法呢?特别简单,我们需要设置一些参数啊,首先的话呢,我们这边是加了一个打印对象,把这个打印对象呢,给它打开了。呃,第二个呢?是为了显示起来的更漂亮,对吧?然后如果是如果是数组的形式呢?进行显示。啊,我们把这些选项呢给它打开了,好,我们来看一下,那这样的话,我们可以通过这个聘命令来去查看这个对象呢,里面的内容。诶,那我们会发现呢?在看这个对象里面内容的时候啊,除了有这个a和b这两个成员变量之外。它还有一个什么东西呢?它还有一个指针啊,这个这个呢地方,它写的叫vpdr点parent,那后面的话呢,有一个地址。啊,有个地址,然后呢?我做了一些事情啊,我把这个地址里面的内容呢打出来看了一下,这个地址里面的内容怎么看的呢?哎,我们通过一个p加杠a。这个杠a呢?表示它是16进制显示啊,就16进制显示新的话呢,表示读这个地址的内容,然后后面的话呢,还有个阿大夫三表示从这个地址开始读。三个数据出来,那这三个数据读出来之后是啥呢啊?那其中有两个的话呢?是我们需要关注的,你会发现一个是八零四八七八四。这个呢,后面有一个标识啊,是我们叫呃parent双冒号vr function 1,第二个是八零四八七b零。后面写的是parent双冒号vr function 2,这两个是什么东西嘞?这两个地址实际上就是我们这两个虚函数的入口地址。也就是说的话呢,在我们嗯创建一个对象的时候啊,如果我们这个类里面有虚假收的话,在我们这个对象创建的时候。它除了有这个a和b,它的本身的成员变量之外,它还会多一个指针,这个指针会指向一个表,这个表里面放的是什么呢?表里面放的就是我们虚函数的入口地址。知道不表里面放的就是我们虚函数的入口地址,你只要呢这个类里面有虚函数的话,你只要用这个类创建对象的话,那我们编译器的话呢,默认情况下。会为这个对象的话呢,增加一个指针,这个指针的话呢,哎,它是指向我们这个循环数表的。我们自己呢,可以看一下咱们刚刚例子当中的代码,是不是也有这样的一个虚函数指针啊?指向呢?我们这个虚函数表。好,我们来看一下。嗯,那我们首先的话呢,通过32位的来看一下啊,我们加个干机好不好?干机增加它的调试信息还记得不?那我们g db啊,然后add out启动它。啊,选它之后,我们可以看一下啊,可以看一下。啊,从第一行可以看一下l1啊,从第一行开始看,这是我们的程序,然后我们设置个断点啊,我设置个断点在什么地方呢?我设这个断点在这个30行的位置,怎么设呢?b30就可以了。这是个单点。OK,好,那我接下来我让我的程序它跑起来。那我接下来在什么位置呢?我就在30这个位置了,是不是我让我的程序呢?执行一下。我已经嗯到31行了,也就是第30行代码已经执行过了,执行过之后呀,我们设置把那些开关给它打开啊。比如说我们可以查看一个对象。啊,我们可以这个pretty。OK,好了,我们接下来看一下这个对象啊po bj。好了,我在看这个po bj的时候呢,大家这个地方呢,它显示了一个。呃,一个a,然后还有一个是一个指针啊,这个static的话呢,我们前面说过啊,这个static是不会占用我们这个对象大小的,它在静态区的嘛。那目前的话呢,主要是这两个人啊,那我再来看一下呢,这个地址里面的内容是什么啊?我们也可以通过这个。打印的方式来打印一下啊。星0x。好,我们把它复制过来。我们后面加上一个阿大夫三。啊,那大家会发现的话呢,我这里面的话呢,是不是有一个什么有一个虚函数的?入库地址吧OK,那这个地方大家知道我为什么只有一个我课件当中是有两个循环数的,而我现在这个例子代码中的是什么?是不是只有一个循环是吧?能看到不?这是一个普通的函数啊,普通的函数呢?它的入口地址不需要记录在我们的虚函数表当中,虚函数的入口地址呢?需要记录在我们的虚函数表当中。好。这样的话呢,大家能不能明白我们目前的话呢?输出来的啊,为什么是这个大小啊?我们程序在输出来的运行一下。输出来的时候为什么是这个大小八,我们自己来分析一下。好了,那么我们看一下实际占用我们大小的位置,这个对象里面的大小的位置好,我们在32位是32位的情况下验证的是吧?是八。好,那我们来思考一下喽,我们思考首先的话呢,这个b是不占用大小的,是不是b不占用大小好,所以它的size应该是由a。a是不是占了它的大小a占几个字节?a是不是占四个字节好?a占四个字节。好a占四个字节,然后b不占它的大小啊,然后我们会发现呢,如果一个类当中的话呢,有虚函数。那我们在创建对象的时候,编译器是不是为这个对象创建一个指针?这个指针是不是指向我们的虚函数表的?那指针占几个字节32位,我们指针占几个字节?指针占几个字节,指针是不是也占四个字节?所以那我们这就算出来就是几个字节,就是八个字节好了,那同样大家能不能算一下64位的呢?如果是64位应该是什么情况呢?如果64位的大小应该是什么情况好,那首先一个a占了几个字节,占了四个字节。但是加上这个vpdr,如果是64位机器上,那这个就不是四个字节了,它这个指针的话是几个字节是八个字节?好,那八个字节的话呢?加在一起是多少个字节是12个字节,但是我们64位机器上是不是要保证八字节对齐啊?所以加上对齐的话啊,有因为有对齐的存在,我们要填充几个字节?我们是把要填充四个字节。因为填入四个字节之后,它会变成多少?变成16。16字节16字节是不是一个八的一个倍数?okay,我们可以验证一下,如果在64位记下来,应该就是16了啊,我们默认记下加。test运行一下。是不是16好了?那这个的话呢?大家要去理解啊,要去理解理解一下呢,我们这个虚函数表呢,它存在。啊,首先要知道它为什么存在啊,然后啊,现在为什么还存在大家不能理解是吧,但是它实际上是存在的啊,那为什么需要存在我们后面讲多态的时候?会讲到它存在的一个价值,那现在的话呢,大家知道我们只要一个类当中的话呢,它有虚函数。那么,这样的话呢?这个类在创建对象的时候,编译器呢?会为这个对象最开始的位置啊,会多一个指针,这个指针的话呢?会指向我们的虚化入表的。首例子好了,那这里面的话呢?最后呢?我们来总结一下啊,总结一下的话呢,我们会发现呢,首先在内章中声明。虚函数时,编译器会为类生成一个虚函数表,虚函数表中存在的是什么呢?是虚函数的入口地址。虚函数表是由编译器自动生成和维护的好维修关键字呢?修饰的成员函数啊,会被编译器放入数函数表当中,那这一点我们刚刚是不是看到了我们的代码中?一个是虚函数,一个不是虚函数,一个有文学关键字修饰,一个没有,那我们刚刚在表当中,是不是只看到了文学关键字修饰的这个函数的入口率值啊?好存在虚函数的时候啊,每个对象中都有一个指向虚函数表的指针啊,我们把它叫做vpdr指针。好了,那这里面的话呢,我们就给大家解释了这个对象大小的话呢,它为什么是这样的一个结果来?各位小伙伴呢,自己理解一下它。

文章来源于自己总结和网络转载,内容如有任何问题,请大佬斧正!联系我