一、 C++ 类对象的内存大小
接下来看一个例子:
class animals{
public:
animals(){
std::cout<<"animals"<用该类实例化一个对象,看一下对象的大小:
![[图片]](https://1000bd.com/contentImg/2023/11/04/120347968.png)
通过测试程序显示这个对象1bytes,这里我就很纳闷?这个类的什么成员占有了他的一个字节?
网上搜索的资料,类的成员函数,静态成员,this指针都是不占内存的,那么为什么这个类是1bytes。
空类的大小为1byte:
原因是为了让对象的实例能够相互区别。空类同样能够被实例化,并且每个实例在内存中都有独一无二的地址,因此,编译器会给空类隐含加一个字节,这样空类实例化之后就会拥有独一无二的内存地址。
如果没有这一个字节的占位,那么空类就无所谓实例化了,因为实例化的过程就是在内存中分配一块地址。
当空类作为另一个类的成员时,大小计算在内。但是当一个类继承空类时:大小不计算,空白基类最优化(EBO/EBCO)
那么上述例子就应该相当于空类。现在我们加上虚函数看一下他的大小。
![[图片]](https://1000bd.com/contentImg/2023/11/04/120348013.png)
上述显示占8个字节,说明加上虚函数之后,生成了新的函数成员且占用内存大小为8 byte,这个成员函数应该就是我们经常说的虚函数指针。因为我用的是64位操作系统,所以他是8byte,当使用32位时,他应该是4byte。
那么通过上述现象就可以解释很多面试时常问的问题,例如在虚函数指针什么时候被生成的,在编译期,还是程序运行的时候?
这里就可以看出虚函数指针是在创建对象的时候,执行了构造函数,虚函数指针才被初始化,而并不是存在于类中的。
那么虚函数指针是干什么用的?
虚函数指针是用来找虚函数表的,虚函数表里存放了虚函数的地址,他可以通过虚函数表找到虚函数的地址,从而调用虚函数。
虚函数表的构建是在编译期,在编译期就构建好了虚函数表,当生成对象执行构造函数时,将虚函数表的首地址赋给虚函数指针,完成虚函数指针的初始化。
二、通过虚函数机制实现类的多态
继承机制,相信大家都学过,据个例子:
class animals{
public:
animals(){
std::cout<<"animals"<我们还是先从内存的机制来看,我们看一下 dog的内存大小:先来new 一下
dog * dg = new dog()

我们现在在基类加一些变量,例如 long weights = 50,现在dog的内存大小:
![[图片]](https://1000bd.com/contentImg/2023/11/04/120347971.png)
这个说明继承机制,会开为父类,以及祖父类的类得空间都会开出来,除了空类。
我们现在再在这个基础上为父类得 cry()函数加上 virtual关键字,让其生成虚函数指针,得到内存大小为16 bytes。
![[图片]](https://1000bd.com/contentImg/2023/11/04/120347994.png)
现在我们再为子类加一个virtual 关键字,看会不会生成一个属于子类的虚函数指针。随便给一个函数添加一个。
![[图片]](https://1000bd.com/contentImg/2023/11/04/120347992.png)
说明子类共享基类的虚函数指针。
那么接下来我们看一个典型的多态的例子:
#include
#include
#include
class animals{
public:
animals(){
std::cout<<"animals"<cry();
an->run();
delete an;
animals * bn = new animals();
bn->cry();*/
animal->cry();
delete animal;
/*animals am = animals();
std::cout << sizeof(animals) << std::endl;
std::cout << "animals obj size = " << sizeof(am) <<" bytes" << std::endl;
*/
return 0;
}
这个典型案例:通过基类定义一个animals 类型的指针,test()实例化一个对象, 用animals类型的指针指向了test实例化的对象,同上,这个操作可以使animal 通过虚函数指针调用test()的虚函数cry(),呢么是如何实现的呢?
虚函数表的生成是在编译阶段,呢么这个继承机制会把虚函数加入一个表,这个表称为虚函数表,虚函数指针的首地址就指向这个表,呢么他是如何找到test cry()的呢,这里是用了一个偏移量来进行寻址的,根据test对象里cry函数的偏移量 + 虚函数指针的首地址,就是我们要找到的函数位置。由于我们已经证明过了,继承的对象中所有函数共享虚函数指针,所以类的多态大概率就是这么来的。