标签:
转自 http://blog.csdn.net/haoel/article/details/1948051/
C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。比如:模板技术,RTTI技术,虚函数技术,要么是试图做到在编译时决议,要么试图做到运行时决议。
关于虚函数的使用方法,我在这里不做过多的阐述。大家可以看看相关的C++的书籍。在这篇文章中,我只想从虚函数的实现机制上面为大家 一个清晰的剖析。
当然,相同的文章在网上也出现过一些了,但我总感觉这些文章不是很容易阅读,大段大段的代码,没有图片,没有详细的说明,没有比较,没有举一反三。不利于学习和阅读,所以这是我想写下这篇文章的原因。也希望大家多给我提意见。
言归正传,让我们一起进入虚函数的世界。
听我扯了那么多,我可以感觉出来你现在可能比以前更加晕头转向了。 没关系,下面就是实际的例子,相信聪明的你一看就明白了。
1 2 3 4 5 6 7 | class Base { public : virtual void f() { cout << "Base::f" << endl; } virtual void g() { cout << "Base::g" << endl; } virtual void h() { cout << "Base::h" << endl; } }; |
按照上面的说法,我们可以通过Base的实例来得到虚函数表。 下面是实际例程:
1 2 3 4 5 6 7 8 9 10 11 12 | typedef void (*Fun)( void ); Base b; Fun pFun = NULL; cout << "虚函数表地址:" << ( int *) (&b) << endl; cout << "虚函数表 — 第一个函数地址:" << ( int *) *( int *) (&b) << endl; /* Invoke the first virtual function */ pFun = (Fun) * ( ( int *) *( int *) (&b) ); pFun(); |
实际运行经果如下:(Windows XP+VS2003, Linux 2.6.22 + GCC 4.1.3)
1 2 3 | (Fun)*(( int *)*( int *)(&b)+0); // Base::f() (Fun)*(( int *)*( int *)(&b)+1); // Base::g() (Fun)*(( int *)*( int *)(&b)+2); // Base::h() |
这个时候你应该懂了吧。什么?还是有点晕。也是,这样的代码看着太乱了。没问题,让我画个图解释一下。如下所示:
下面,再让我们来看看继承时的虚函数表是什么样的。假设有如下所示的一个继承关系:
请注意,在这个继承关系中,子类没有重载任何父类的函数。那么,在派生类的实例中,其虚函数表如下所示:
我相信聪明的你一定可以参考前面的那个程序,来编写一段程序来验证。
覆盖父类的虚函数是很显然的事情,不然,虚函数就变得毫无意义。下面,我们来看一下,如果子类中有虚函数重载了父类的虚函数,会是一个什么样子?假设,我们有下面这样的一个继承关系。
为了让大家看到被继承过后的效果,在这个类的设计中,我只覆盖了父类的一个函数:f()。那么,对于派生类的实例,其虚函数表会是下面的一个样子:
1 2 | Base *b = new Derive(); b->f(); |
由b所指的内存中的虚函数表的f()的位置已经被Derive::f()函数地址所取代,于是在实际调用发生时,是Derive::f()被调用了。这就实现了多态。
下面,再让我们来看看多重继承中的情况,假设有下面这样一个类的继承关系。注意:子类并没有覆盖父类的函数。
2) 子类的成员函数被放到了第一个父类的表中。(所谓的第一个父类是按照声明顺序来判断的)
这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。
我们可以看见,三个父类虚函数表中的f()的位置被替换成了子类的函数指针。这样,我们就可以任一静态类型的父类来指向子类,并调用子类的f()了。如:
1 2 3 4 5 6 7 8 9 10 11 | Derive d; Base1 *b1 = &d; Base2 *b2 = &d; Base3 *b3 = &d; b1->f(); /* Derive::f() */ b2->f(); /* Derive::f() */ b3->f(); /* Derive::f() */ b1->g(); /* Base1::g() */ b2->g(); /* Base2::g() */ b3->g(); /* Base3::g() */ |
每次写C++的文章,总免不了要批判一下C++。这篇文章也不例外。通过上面的讲述,相信我们对虚函数表有一个比较细致的了解了。水可载舟,亦可覆舟。下面,让我们来看看我们可以用虚函数表来干点什么坏事吧。
1 2 | Base1 *b1 = new Derive(); b1->f1(); /* 编译出错 */ |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | class Base { private : virtual void f() { cout << "Base::f" << endl; } }; class Derive : public Base { }; typedef void (*Fun)( void ); void main() { Derive d; Fun pFun = (Fun) * ( ( int *) *( int *) (&d) + 0); pFun(); } |
在文章束之前还是介绍一下自己吧。我从事软件研发有十个年头了,目前是软件开发技术主管,技术方面,主攻Unix/C/C++,比较喜欢网络上的技术,比如分布式计算,网格计算,P2P,Ajax等一切和互联网相关的东西。管理方面比较擅长于团队建设,技术趋势分析,项目管理。欢迎大家和我交流,我的MSN和Email是:haoel@hotmail.com
我们可以在VC的IDE环境中的Debug状态下展开类的实例就可以看到虚函数表了(并不是很完整的)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | #include <iostream> using namespace std; class Base1 { public : virtual void f() { cout << "Base1::f" << endl; } virtual void g() { cout << "Base1::g" << endl; } virtual void h() { cout << "Base1::h" << endl; } }; class Base2 { public : virtual void f() { cout << "Base2::f" << endl; } virtual void g() { cout << "Base2::g" << endl; } virtual void h() { cout << "Base2::h" << endl; } }; class Base3 { public : virtual void f() { cout << "Base3::f" << endl; } virtual void g() { cout << "Base3::g" << endl; } virtual void h() { cout << "Base3::h" << endl; } }; class Derive : public Base1, public Base2, public Base3 { public : virtual void f() { cout << "Derive::f" << endl; } virtual void g1() { cout << "Derive::g1" << endl; } }; typedef void (*Fun)( void ); int main() { Fun pFun = NULL; Derive d; int ** pVtab = ( int * *) &d; /* * Base1‘s vtable * pFun = (Fun)*((int*)*(int*)((int*)&d+0)+0); */ pFun = (Fun) pVtab[0][0]; pFun(); /* pFun = (Fun)*((int*)*(int*)((int*)&d+0)+1); */ pFun = (Fun) pVtab[0][1]; pFun(); /* pFun = (Fun)*((int*)*(int*)((int*)&d+0)+2); */ pFun = (Fun) pVtab[0][2]; pFun(); /* * Derive‘s vtable * pFun = (Fun)*((int*)*(int*)((int*)&d+0)+3); */ pFun = (Fun) pVtab[0][3]; pFun(); /* The tail of the vtable */ pFun = (Fun) pVtab[0][4]; cout << pFun << endl; /* * Base2‘s vtable * pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0); */ pFun = (Fun) pVtab[1][0]; pFun(); /* pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1); */ pFun = (Fun) pVtab[1][1]; pFun(); pFun = (Fun) pVtab[1][2]; pFun(); /* The tail of the vtable */ pFun = (Fun) pVtab[1][3]; cout << pFun << endl; /* * Base3‘s vtable * pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0); */ pFun = (Fun) pVtab[2][0]; pFun(); /* pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1); */ pFun = (Fun) pVtab[2][1]; pFun(); pFun = (Fun) pVtab[2][2]; pFun(); /* The tail of the vtable */ pFun = (Fun) pVtab[2][3]; cout << pFun << endl; return (0); } |
标签:
原文地址:http://www.cnblogs.com/zhxshseu/p/5294026.html