多态性----vptr----vtable

转载 2016年05月30日 20:05:22

from:http://www.cppblog.com/fwxjj/archive/2007/01/25/17996.html

多态性 (polymorphism) 是面向对象编程的基本特征之一。而在 C++ 中,多态性通过虚函数 (virtual function) 来实现。我们来看一段简单的代码:

  #include <iostream>
  using namespace std;
  class Base
  {
  int a;
  public:
  virtual void fun1() {cout<<"Base::fun1()"<<endl;}
  virtual void fun2() {cout<<"Base::fun2()"<<endl;}
  virtual void fun3() {cout<<"Base::fun3()"<<endl;}
  };
  class A:public Base
  {
  int a;
  public:
  void fun1() {cout<<"A::fun1()"<<endl;}
  void fun2() {cout<<"A::fun2()"<<endl;}
  };
  void foo (Base& obj)
  {
  obj.fun1();
  obj.fun2();
  obj.fun3();
  }
  int main()
  {
  Base b;
  A a;
  foo(b);
  foo(a);
  }
  运行结果为:
  Base::fun1()
  Base::fun2()
  Base::fun3()
  A::fun1()
  A::fun2()
  Base::fun3() 
  仅通过基类的接口,程序调用了正确的函数,它就好像知道我们输入的对象的类型一样!
  那么,编译器是如何知道正确代码的位置的呢?其实,编译器在编译时并不知道要调用的函数体的正确位置,但它插入了一段能找到正确的函数体的代码。这称之为 晚捆绑 (late binding) 或 运行时捆绑 (runtime binding) 技术。
  通过virtual 关键字创建虚函数能引发晚捆绑,编译器在幕后完成了实现晚捆绑的必要机制。它对每个包含虚函数的类创建一个表(称为VTABLE),用于放置虚函数的地址。在每个包含虚函数的类中,编译器秘密地放置了一个称之为vpointer(缩写为VPTR)的指针,指向这个对象的VTABLE。所以无论这个对象包含一个或是多少虚函数,编译器都只放置一个VPTR即可。VPTR由编译器在构造函数中秘密地插入的代码来完成初始化,指向相应的VTABLE,这样对象就“知道”自己是什么类型了。 VPTR都在对象的相同位置,常常是对象的开头。这样,编译器可以容易地找到对象的VTABLE并获取函数体的地址。
  如果我们用sizeof查看前面Base类的长度,我们就会发现,它的长度不仅仅是一个int的长度,而是增加了刚好是一个void指针的长度(在我的机器里面,一个int占4个字节,一个void指针占4个字节,这样正好类Base的长度为8个字节)。
  每当创建一个包含虚函数的类或从包含虚函数的类派生一个类时,编译器就为这个类创建一个唯一的VTABLE。在VTABLE中,放置了这个类中或是它的基类中所有虚函数的地址,这些虚函数的顺序都是一样的,所以通过偏移量可以容易地找到所需的函数体的地址。假如在派生类中没有对在基类中的某个虚函数进行重写(overriding),那末还使用基类的这个虚函数的地址(正如上面的程序结果所示)。
  

  至今为止,一切顺利。下面,我们的试验开始了。
  就目前得知的,我们可以试探着通过自己的代码来调用虚函数,也就是说我们要找寻一下编译器秘密地插入的那段能找到正确函数体的代码的足迹。
  如果我们有一个Base指针作为接口,它一定指向一个Base或由Base派生的对象,或者是A,或者是其它什么。这无关紧要,因为VPTR的位置都一样,一般都在对象的开头。如果是这样的话,那么包含有虚函数的对象的指针,例如Base指针,指向的位置恰恰是另一个指针——VPTR。VPTR指向的 VTABLE其实就是一个函数指针的数组,现在,VPTR正指向它的第一个元素,那是一个函数指针。如果VPTR向后偏移一个Void指针长度的话,那么它应该指向了VTABLE中的第二个函数指针了。
  这看来就像是一个指针连成的链,我们得从当前指针获取它指向的下一个指针,这样我们才能“顺藤摸瓜”。那么,我来介绍一个函数:
  void *getp (void* p)
  {
  return (void*)*(unsigned long*)p;
  }
  我们不考虑它漂亮与否,我们只是试验。getp() 可以从当前指针获取它指向的下一个指针。如果我们能找到函数体的地址,用什么来存储它呢?我想应该用一个函数指针:
  typedef void (*fun)();
  它与Base中的三个虚函数相似,为了简单我们不要任何输入和返回,我们只要知道它实际上被执行了即可。
  然后,我们负责“摸瓜”的函数登场了:
  fun getfun (Base* obj, unsigned long off)
  {
  void *vptr = getp(obj);
  unsigned char *p = (unsigned char *)vptr;
  p += sizeof(void*) * off;
  return (fun)getp(p);
  }
  第一个参数是Base指针,我们可以输入Base或是Base派生对象的指针。第二个参数是VTABLE偏移量,偏移量如果是0那么对应fun1(),如果是1对应fun2()。getfun() 返回的是fun类型函数指针,我们上面定义的那个。可以看到,函数首先就对Base指针调用了一次getp(),这样得到了vptr这个指针,然后用一个 unsigned char指针运算偏移量,得到的结果再次输入getp(),这次得到的就应该是正确的函数体的位置了。
  那么它到底能不能正确工作呢?我们修改main() 来测试一下:
  int main()
  {
  Base *p = new A;
  fun f = getfun(p, 0);
  (*f)();
  f = getfun(p, 1);
  (*f)();
  f = getfun(p, 2);
  (*f)();
  delete p;
  }
  激动人心的时刻到来了,让我们运行它!
  运行结果为:
  A::fun1()
  A::fun2()
  Base::fun3()
  至此,我们真的成功了。通过我们的方法,我们获取了对象的VPTR,在它的体外执行了它的虚函数。

多态性----vptr----vtable

多态性 (polymorphism) 是面向对象编程的基本特征之一。而在 C++ 中,多态性通过虚函数 (virtual function) 来实现。我们来看一段简单的代码:  #include   ...
  • dongzhiquan
  • dongzhiquan
  • 2009年08月05日 16:52
  • 356

【转】C++ 多态性----vptr----vtable

多态性 (polymorphism) 是面向对象编程的基本特征之一。而在 C++ 中,多态性通过虚函数 (virtual function) 来实现。我们来看一段简单的代码:#include usin...
  • zjn0430
  • zjn0430
  • 2011年02月18日 17:19
  • 462

转:多态性----vptr----vtable

多态性----vptr----vtable 多态性 (polymorphism) 是面向对象编程的基本特征之一。而在 C++ 中,多态性通过虚函数 (virtual function) 来实现。我们...
  • anhuidelinger
  • anhuidelinger
  • 2013年04月03日 20:07
  • 405

SDUT2679 6-1 多态性与虚函数

6-1 多态性与虚函数#include using namespace std;class Pet { public: virtual void speak()//定义基类函数为虚函数 ...
  • yue_luo_
  • yue_luo_
  • 2016年10月28日 15:40
  • 499

面向对象的特点,封装性,继承性,多态性!

1.面向对象程序设计有三大特点,分别是封装,继承,多态,接下来就介绍一下这三个特点,积累一下。 2.封装,封装是面向对象编程的核心思想,将对象的属性和行为封装起来,而将对象的属性和行为封装起来的载体是...
  • u012561176
  • u012561176
  • 2015年01月29日 19:37
  • 1644

C++ 多态性有哪些

C++多态性有哪些?       概念:指相同的对象收到不同的消息或者不同的对象收到相同的消息时产生的不同的实现动作。 C++支持两种多态:编译时多态(静态)、运行时多态(动态) (1)编译时多态...
  • YF_Li123
  • YF_Li123
  • 2017年07月03日 21:50
  • 1445

面向对象-多态性

 面向对象的软件开发语言具有三个重要的特点分别为封装性、继承性、多态性。封装性即意味着对象封装其内部的数据,使其对外不可见,以保证数据的安全性。继承性是代码复用的一个很好的解决方案,但是继承关系是编译...
  • slg_sparkler
  • slg_sparkler
  • 2007年03月21日 22:28
  • 2201

关于C++多态性的一些总结

在学习过程中对多态特性的实现方式及其实现原理的一些总结
  • Phoenix500526
  • Phoenix500526
  • 2015年03月31日 07:59
  • 841

对象多态性的理解

面向对象的三大特性:封装—保护类中的属性不被外部直接访问到;继承—扩展类的属性和功能;那么多态性呢?多态性是Java中最强悍的地方,那么有一个简单但是又需要好好推敲的疑问:什么是多态?什么是对象的多态...
  • jakezhang1990
  • jakezhang1990
  • 2017年03月30日 22:24
  • 836

C++多态性的理解和举例

多态性是面向对象c'x'de
  • lanzhihui_10086
  • lanzhihui_10086
  • 2014年08月05日 20:43
  • 809
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:多态性----vptr----vtable
举报原因:
原因补充:

(最多只允许输入30个字)