关于虚函数,类的内存分布以及类的成员函数调用原理
1.类的内存分布
空类为了占位,空间占一个字节
成员函数,静态函数,静态变量并不占内存(不和类放在一起)
所有的虚函数也并不和类放在一起,而是将所有的虚函数构造成一个虚函数表,用一个指针指向这个虚函数表,类仅仅存储这个指针,一个指针在32位的机器上占四个字节
所有的非静态成员变量占内存
因此,类的内存分布=所有的非静态成员变量+虚指针(自创的名词:即指向虚函数表的指针)
2.虚函数的原理
一个非继承的类:一个虚指针(指向他的虚函数表)、
一个单继承的类:一个虚指针(指向他的虚函数表,这个虚函数表=复制自父类的虚函数表(更新自己重写的部分虚函数,没有更新的直接复制)+新的自己的虚函数)
一个多继承的类:多个虚指针:
一个主虚指针:存放着复制自主基类的虚函数表(更新自己重写的部分虚函数,没有更新的直接复制)+新的自己的虚函数
多个从虚指针:存放着复制自其它基类的虚函数表(更新自己重写的部分虚函数,没有更更新的直接复制)
3.函数调用原理
以前都理解错了啊,其实函数调用原理和->运算符和.运算符并没由任何关系
函数调用其实是有两种:动态绑定和静态绑定
前者相信很多人面试的时候都被问到过!然而我以前只知道个皮毛!!!!
所谓动态绑定就是一个类的指针或者引用在调用一个虚函数的时候,并不直接指向该函数的地址,而是指向这个类的虚函数表,比如
A& a;
a->func();//假设func()是类A的虚函数
这里的调用关系实质是A::this->vfptr;//编译阶段就调到这里,到运行时,就vfptr->func()
这就是所谓的动态绑定
而静态绑定就是
A a;
a.func();//假设func是个虚函数
这里的调用关系就是直接指向func()函数的地址,不通过A::this->vfptr,而是直接指向func()的地址
另外:这个例子中a.func()调用的是A类中的虚函数(假设已重写),而不是父类中的虚函数,
总结:
假设用得是.运算符,则肯定是静态绑定,不管是不是虚函数
假设用得是->运算符,且调用函数是虚函数,则肯定是动态绑定,若不是虚函数,肯定不是动态绑定
有题为证(一道笔试题引发的血案,深入探讨了所谓的动态绑定)
4、以下代码
[cpp] view plaincopy
class classA
{
public:
classA()
{
clear();
}
virtual ~classA()
{
}
void clear()
{
memset(this , 0 , sizeof(*this));
}
virtual void func()
{
printf("func\n");
}
};
class classB : public classA
{
};
int main(void)
{
classA oa;
classB ob;
classA * pa0 = &oa;
classA * pa1 = &ob;
classB * pb = &ob;
oa.func(); // 1
ob.func(); // 2
pa0->func(); // 3
pa1->func(); // 4
pb->func(); // 5
return 0;
}
A、func func 执行出错 执行出错 func
B、执行出错 func 执行出错 执行出错 func
C、执行出错 执行出错 执行出错 执行出错 执行出错
D、func func func func func
E、func func 执行出错 func func
F、以上选项都不对