关于虚函数,类的内存分布以及类的成员函数调用原理

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、以上选项都不对

posted @ 2017-02-26 14:58  LT.C#  阅读(1128)  评论(0编辑  收藏  举报