C++的三大特性之一的多态是基于虚函数实现的,而大部分编译器是采用虚函数表来实现虚函数,虚函数表(VTAB)存在于可执行文件的只读数据段中,指向VTAB的虚表指针(VPTR)是包含在类的每一个实例当中。当使用引用或指针调用虚函数时,首先通过VPTR找到VTAB,然后通过偏移量找到虚函数地址并调用。

 

本文参考:1.http://blog.lucode.net/programming-language/cpp-vtab-and-call-convention.html

  2.https://blog.csdn.net/tangaowen/article/details/5830803

  3.《深度探索C++对象模型》

一、单继承

  1. 1 #include<iostream>
  2. 2 #include <stdio.h>
  3. 3 using namespace std;
  4. 4 class A {
  5. 5 public:
  6. 6 void func() {
  7. 7 cout << "A::func()" << endl;
  8. 8 }
  9. 9 virtual void func1() {
  10. 10 cout << "A::func1(): " << endl;
  11. 11 }
  12. 12
  13. 13 virtual void func3() {
  14. 14 cout << "A::func3(): " << endl;
  15. 15 }
  16. 16 };
  17. 17
  18. 18 class B: public A {
  19. 19 public:
  20. 20 virtual void func() {
  21. 21 cout << "B::func()" << endl;
  22. 22 }
  23. 23 virtual void vfunc() {
  24. 24 cout << "B::vfunc()" << endl;
  25. 25 }
  26. 26 void func1() {
  27. 27 cout << "B::func1(): " << endl;
  28. 28 }
  29. 29 };
  30. 30 int main() {
  31. 31 typedef void (*Fun)(void);
  32. 32 B a;
  33. 33
  34. 34 Fun *fun = NULL;
  35. 35 fun = (Fun*) ((int *) *(int *) &a);
  36. 36 // fun = *(Fun **) &a;
  37. 37 fun[0]();
  38. 38 fun[1]();
  39. 39 fun[2]();
  40. 40 fun[3]();
  41. 41
  42. 42 return 0;
  43. 43 }

运行结果:

B::func1():
A::func3():
B::func()
B::vfunc()

二、多重继承

  1. 1 #include<iostream>
  2. 2 #include <stdio.h>
  3. 3 using namespace std;
  4. 4 class B1 {
  5. 5 public:
  6. 6 virtual void barB1() {cout << "B1::bar" << endl;}
  7. 7 virtual void fooB1() {cout << "B1::foo" << endl;}
  8. 8 };
  9. 9
  10. 10 class B2 {
  11. 11 public:
  12. 12 virtual void barB2() {cout << "B2::bar" << endl;}
  13. 13 virtual void fooB2() {cout << "B2::foo" << endl;}
  14. 14 };
  15. 15
  16. 16 class D : public B1, B2 {
  17. 17 public:
  18. 18 void fooB1() {cout << "D::foo" << endl;}
  19. 19 void barB2() {cout << "D::bar" << endl;}
  20. 20 };
  21. 21
  22. 22 typedef void (*Func)();
  23. 23 int main() {
  24. 24 D tt;
  25. 25 Func* vptr1 = *(Func**)&tt;
  26. 26 Func* vptr2 = *((Func**)&tt + 1);
  27. 27
  28. 28 vptr1[0]();
  29. 29 vptr1[1]();
  30. 30 vptr1[2]();
  31. 31 cout<<"\\\\\\\\\\\\"<<endl;
  32. 32 vptr2[0]();
  33. 33 vptr2[1]();
  34. 34
  35. 35 return 0;
  36. 36 }

运行结果:

B1::bar
D::foo
D::bar
\\\\\\
D::bar
B2::foo

结论:

     多重继承会有多个虚函数表,几重继承,就会有几个虚函数表。这些表按照派生的顺序依次排列,如果子类改写了父类的虚函数,那么就会用子类自己的虚函数覆盖虚函数表的相应的位置,如果子类有新的虚函数,那么就添加到第一个虚函数表的末尾。

 

再简单总结一下 覆盖 隐藏 重载 的区别:

覆盖 是C++虚函数的实现原理,基类的虚函数被子类重写,要求函数参数列表相同;

隐藏 是C++的名字解析过程,分两种情况,基类函数有virtual,参数列表不同,或基类函数没有virtual,无论参数列表是否相同。此时基类指针指向基类实例则调用基类函数,指向子类则调用子类函数。

重载 是在同一命名空间中根据参数对同名函数的区别。

 

版权声明:本文为bobojiang原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/bobojiang/p/11282446.html