虚函数的作用主要是实现了多态的机制。简单来说,就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这样子就可以让父类的指针有“多种形态”,这是一种泛型技术。就是试图使用不变的代码来实现可变的算法。
每个对象占用存储空间的只是该对象的数据部分,函数代码属于公用部分。
参考博文:https://www.cnblogs.com/zhxmdefj/p/11594459
C++内存分区分为五个部分:
1.栈:由编译器在需要的时候自动分配,不需要时自动清除变量存储区,通常存放局部变量,函数参数等。
2.堆:是由malloc等分配的内存块,和堆十分相似,用free来释放。
3.自由存储区:由new分配的内存块,由程序员释放,如果程序员没有释放掉,资源将由操作系统在程序结束后自动回收。
4.全局/静态存储区:全局变量和静态变量被分配到同一块内存中
5.常量存储区:这是一块特殊存储区,里边存放常量,不允许修改
(堆的自由存储区其实不过是同一块区域,new底层实现代码中调用了malloc,new可以看成是malloc智能化的高级版本)
静态成员函数和非静态成员函数都是在类的定义时放在内存的代码区的,都是属于类,但是类为什么只能直接带哦用静态类成员函数,而非静态类成员函数(即使函数没有参数)只有类对象才能调用呢。
原因是:类的非静态类成员函数其实都包含了一个指向类对象的指针型参数(即this指针),因此只有类对象才能调用(此时this指针有实值)
虚函数表
C++通过继承和虚函数来实现多态性,虚函数通过一张虚函数表来实现的,虚函数表解决了继承,覆盖,添加虚函数的问题,保证了其真实反应实际的函数。
原理:
通过为每一个类对象添加一个隐藏成员,隐藏成员保存了一个指针,这个指针叫虚表指针,它指向了一个虚函数表。
虚函数表就像一个数组一样,表中有许多的槽,每个槽中存放的是一个虚函数的地址(也就是数组中存放着指向每个虚函数的指针),因此每个类都使用一个虚函数表,每个类对象用一个虚表指针。
在有虚函数的类的实例化对象中,这个表被分配在了这个实例对象的内存中,用父类的指针来操作一个子类的时候,虚函数表就像一个地图一样,指明了实际应该调用的函数。
例如:
基类对象包含一个虚表指针,指向基类的虚函数表
派生类对象也将包含一个虚表指针,指向派生类虚函数表
1.如果派生类重写了基类的虚方法,该派生类虚函数表将保存重写的虚函数的地址,而不是基类的虚函数的地址
2.如果基类中的虚方法没有在派生类中重写,那么派生类将继承基类中的虚方法,而且派生类中虚函数表将保存
基类中未被重写的虚函数地址,但是如果派生类中定义了心得虚方法,则该虚函数的地址也将被添加到派生类虚函数表中。
找到虚函数表
C++的编译器会保证虚函数表的指针存在于对象实例中最前面的位置(为了保证取虚函数表有着最高的性能,在有多层继承
或者是多重继承的情况下),意味着我们通过对象实例的地址得到这种虚函数表的地址,然后就可以遍历其中函数指针,
并调用响应的函数。
单继承(无覆盖)
1.虚函数按照其声明顺序放于表中
2.父类的虚函数在子类的虚函数前
单继承(有覆盖)
1. 覆盖的函数被放到了虚表中原来父类虚函数的位置
2.没有被覆盖的函数依旧
多重继承(无覆盖)
多继承下,虚函数表存储方式发生了变化,C++编译器在对象内加入了一个隐藏成员,现在你可以理解为,在多继承时加入了
多个隐藏成员,也就是说有多个虚函数表。
多重继承(有覆盖)
多继承下,虚函数表存储方式会发生变化,并且覆盖的函数被放到了虚表中原来父类虚函数的位置,没有被覆盖的函数依旧,但是有多个虚函数表
虚函数表会引发的安全性问题
1.通过父类型的指针访问子类自己的虚函数
2.访问非public的虚函数
标签:虚表,函数,以及,对象,派生类,基类,指针 From: https://www.cnblogs.com/huwy-123/p/18124590