虚函数是面向对象编程语言里一个很重要的机制,下面我们以一个c++例子,分析其对应的c语言程序来说明虚函数的机制。
面向对象有了一个重要的概念就是对象的实例,对象的实例代表一个具体的对象,故其肯定有一个数据结构保存这实例的数据,这一数据包括变量,接口函数指针,如果是虚函数,则有相应的虚函数指针,其他函数指针不包括。
要讲虚函数机制,必须讲继承,因为只有继承才有虚函数的动态绑定功能,先讲下c++继承对象实例内存分配基础知识:
c++继承分为两种,普通继承和虚拟继承(virtual)。具体的继承又根据父类中的函数是否virtual而不同。
下面就单继承分为几种情况阐述:
1.普通继承+父类无virtual函数
若子类没有新定义virtual函数 此时子类的布局是 : 由低地址->高地址 为父类的元素(没有vptr),子类的元素(没有vptr).
若子类有新定义virtual函数 此时子类的布局是 : 由低地址->高地址 为父类的元素(没有vptr),子类的元素(包含vptr,指向vtable.)
2. 普通继承+父类有virtual函数
不管子类没有新定义virtual函数 此时子类的布局是 : 由低地址->高地址 为父类的元素(包含vptr), 子类的元素.
如果子类有新定义的virtual函数,那么在父类的vptr(也就是第一个vptr)对应的vtable中添加一个函数指针.
3.virtual继承
若子类没有新定义virtual函数 此时子类的布局是 : 由低地址->高地址子类的元素(有vptr),虚基类的元素.为什么这里会出现vptr,因为虚基类派生出来的类中,虚类的对象不在固定位置(猜测应该是在内存的尾部),需要一个中介才能访问虚类的对象.所以虽然没有virtual函数,子类也需要有一个vptr,对应的vtable中需要有一项指向 虚基类.
若子类有新定义virtual函数 此时子类的布局是与没有定义新virtual函数内存布局一致.但是在vtable中会多出新增的虚函数的指针.
4.多重继承
此时子类的布局是 : 由低地址->高地址 为父类p1的元素(p1按照实际情况确定元素中是否包含vptr), 父类p2的元素(p2按照实际情况确定元素中是否包含vptr),子类的元素.
如果所有父类都没有vptr,那么如果子类定义了新的virtual function,那么子类的元素中会有vptr,对应的vtable会有相应的函数指针.
如果有的父类存在vptr.如果子类定义了新的virtual function,会生成一个子类的vtable,这个子类的vtable是,在它的父类的vtable中后添加这个新的虚函数指针生成的.因为子类分配的空间显示并没有新增加一个4字节的指针空间,其实不管子类增加了多少新的虚函数,其空间大小不变,因为其和虚函数相关的分配的空间就是一个vptr,是一个指针,也就是4字节,不变,要变是变在vtable.
比如如下一个类:
Class test1() {};
fun1() {};
public Virtual a(){println(“test1:a”);};
public Virtual b(int b){println(“test1:b”);};
int a;
}
Class test2 extends test1{
fun2(){};
public Virtual b(int b){this->b++;println(“test2:b”) } ;
public Vitrual c(){println(“test2:c}”)}
int b;
}
Int main(){
test1 a=new test2();
a.b();
}