当前位置:   article > 正文

C++:87---类继承(虚函数表:Virtual Table)_virtual 子类和父类virtualtable

virtual 子类和父类virtualtable

一、虚函数表解析

  • 虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。在这个表中,主要是一个类的虚函数的地址表,这张表解决了继承、覆盖(重写)的问题
  • 在有虚函数的类中,虚函数表被分配在类实例的内存中,所以当用父类的指针来操作一个子类的时候,这张虚函数表就显得非常重要了
  • 虚函数表类似于一张地图,指明了实际所应该调用的函数
  • C++标准说明书中说到,编译器必须保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证正确取到虚函数的偏移量)。这意味着通过对象实例的地址得到这张虚函数表,之后就可以遍历其中的函数指针,并调用相应的函数
  • 关于虚函数表的设计还可以参阅《C++对象模型》:https://blog.csdn.net/qq_41453285/article/details/104084764

二、无继承下的虚函数表解析

  1. #include <iostream>
  2. using namespace std;
  3. class Base
  4. {
  5. public:
  6. virtual void func1() { cout << "Base::fun1" << endl;}
  7. virtual void func2() { cout << "Base::fun2" << endl;}
  8. virtual void func3() { cout << "Base::fun3" << endl;}
  9. private:
  10. int num1;
  11. int num2;
  12. };
  13. // 函数指针
  14. typedef void (*Fun)();
  15. int main()
  16. {
  17. Base b;
  18. Fun pFun;
  19. //取得Base::func1()的地址
  20. pFun = (Fun)*((int*)*(int*)(&b) + 0);
  21. pFun();
  22. //取得Base::func2()的地址
  23. pFun = (Fun)*((int*)*(int*)(&b) + 1);
  24. pFun();
  25. //取得Base::func3()的地址
  26. pFun = (Fun)*((int*)*(int*)(&b) + 2);
  27. pFun();
  28. return 0;
  29. }
  • 上面的代码可能编译不通过,由于编译器的原因
  • 转换解析:
  1. (Fun)*((int*)*(int*)(&b) + 0);
  2. // 取得对象b的地址,转换为int*, 又因为虚函数表在类的其实位置, 因此这一步也是取得虚函数表的地址
  3. (int*)(&b);
  4. // 偏移都虚函数表的第一个函数处
  5. (int*)(&b) + 0;
  6. // 取得第一个函数的地址
  7. *(int*)(&b) + 0;
  8. // 将第一个函数的地址转换为int*
  9. (int*)*(int*)(&b) + 0;
  10. // 取得地址中的函数
  11. *((int*)*(int*)(&b) + 0);
  12. // 转换为Fun函数
  13. (Fun)*((int*)*(int*)(&b) + 0);
  • Base类的内存结构如下所示:
    • vfptr:虚函数表指针
    • num1、num2:数据成员

三、单一继承(无重写(覆盖))

  • 下面是单一继承中虚函数表的设计,并且派生类没有重写(覆盖)基类的虚函数
  1. #include <iostream>
  2. using namespace std;
  3. class Base1
  4. {
  5. public:
  6. Base1(int num) : num_1(num) { }
  7. virtual void foo1() { cout << "Base1::foo1" << endl;}
  8. virtual void foo2() { cout << "Base1::foo2" << endl;}
  9. virtual void foo3() { cout << "Base1::foo3" << endl;}
  10. private:
  11. int num_1;
  12. };
  13. class Derived1 : public Base1
  14. {
  15. public:
  16. Derived1(int num) : Base1(num) { }
  17. virtual void faa1() { cout << "Derived1::faa1" << endl;}
  18. virtual void faa2() { cout << "Derived1::faa2" << endl;}
  19. };
  • Derived1继承自Base1类,其虚函数表如下所示:
    • 前面3个为基类Base1的虚函数
    • 后面两个为Derived1自己的虚函数

四、单一继承(有重写(覆盖))

  • 下面是单一继承中虚函数表的设计,并且派生类重写(覆盖)了基类的虚函数
  1. #include <iostream>
  2. using namespace std;
  3. class Base1
  4. {
  5. public:
  6. Base1(int num) : num_1(num) { }
  7. virtual void foo1() { cout << "Base1::foo1" << endl;}
  8. virtual void foo2() { cout << "Base1::foo2" << endl;}
  9. virtual void foo3() { cout << "Base1::foo3" << endl;}
  10. private:
  11. int num_1;
  12. };
  13. class Derived2 : public Base1
  14. {
  15. public:
  16. Derived2(int num) : Base1(num) { }
  17. virtual void foo2() { cout << "Derived2::foo2" << endl;} //重写了基类的foo2虚函数
  18. virtual void fbb2() { cout << "Derived2::fbb2" << endl;}
  19. virtual void fbb3() { cout << "Derived2::fbb3" << endl;}
  20. };
  • Derived2继承自Base1类,其虚函数表如下所示:
    • 第1个、第3个为基类Base1的虚函数
    • 第2个本来是基类Base1的虚函数,由于其重写了基类的foo2()虚函数,因此需要更换为自己的虚函数指针
    • 后面两个为Derived2自己的虚函数

  • 当我们通过基类指针指向于派生类时,调用其中的函数就是根据这张虚函数表来调用的
  1. Derived2 *p = new Base1(1);
  2. p->foo2(); //调用派生类的foo2()函数

五、多重继承(无重写(覆盖))

  • 下面是多重承中虚函数表的设计,并且派生类没有重写(覆盖)基类的虚函数
  1. #include <iostream>
  2. using namespace std;
  3. class Base1
  4. {
  5. public:
  6. Base1(int num) : num_1(num) { }
  7. virtual void foo1() { cout << "Base1::foo1" << endl;}
  8. virtual void foo2() { cout << "Base1::foo2" << endl;}
  9. virtual void foo3() { cout << "Base1::foo3" << endl;}
  10. private:
  11. int num_1;
  12. };
  13. class Base2
  14. {
  15. public:
  16. Base2(int num) : num_2(num) { }
  17. virtual void foo1() { cout << "Base2::foo1" << endl;}
  18. virtual void foo2() { cout << "Base2::foo2" << endl;}
  19. virtual void foo3() { cout << "Base2::foo3" << endl;}
  20. private:
  21. int num_2;
  22. };
  23. class Base3
  24. {
  25. public:
  26. Base3(int num) : num_3(num) { }
  27. virtual void foo1() { cout << "Base3::foo1" << endl;}
  28. virtual void foo2() { cout << "Base3::foo2" << endl;}
  29. virtual void foo3() { cout << "Base3::foo3" << endl;}
  30. private:
  31. int num_3;
  32. };
  33. class Derived3 : public Base1, public Base2, public Base3
  34. {
  35. public:
  36. Derived3(int num1, int num2, int num3) : Base1(num1), Base2(num2), Base3(num3) { }
  37. virtual void fcc1() { cout << "Derived3::fcc1" << endl;}
  38. virtual void fcc2() { cout << "Derived3::fcc2" << endl;}
  39. };
  • Derived2继承自Base1、Base2、Base3类,其虚函数表如下所示:
    • Derived继承3个基类,其中为每一个基类都涉及了一张虚函数表

  • 为每一个基类都涉及了一张虚函数表的目的是:为了解决不同的父类类型的指针指向同一个子类实例,而能够调用实际的函数。例如:
  1. Base2 *pBase2 = new Derived3();
  2. pBase2->foo2(); //调用Base2::foo2()

六、多重继承(有重写(覆盖))

  • 下面是多重承中虚函数表的设计,并且派生类重写(覆盖)了基类的虚函数
  1. #include <iostream>
  2. using namespace std;
  3. class Base1
  4. {
  5. public:
  6. Base1(int num) : num_1(num) { }
  7. virtual void foo1() { cout << "Base1::foo1" << endl;}
  8. virtual void foo2() { cout << "Base1::foo2" << endl;}
  9. virtual void foo3() { cout << "Base1::foo3" << endl;}
  10. private:
  11. int num_1;
  12. };
  13. class Base2
  14. {
  15. public:
  16. Base2(int num) : num_2(num) { }
  17. virtual void foo1() { cout << "Base2::foo1" << endl;}
  18. virtual void foo2() { cout << "Base2::foo2" << endl;}
  19. virtual void foo3() { cout << "Base2::foo3" << endl;}
  20. private:
  21. int num_2;
  22. };
  23. class Base3
  24. {
  25. public:
  26. Base3(int num) : num_3(num) { }
  27. virtual void foo1() { cout << "Base3::foo1" << endl;}
  28. virtual void foo2() { cout << "Base3::foo2" << endl;}
  29. virtual void foo3() { cout << "Base3::foo3" << endl;}
  30. private:
  31. int num_3;
  32. };
  33. class Derived4 : public Base1, public Base2, public Base3
  34. {
  35. public:
  36. Derived4(int num1, int num2, int num3) : Base1(num1), Base2(num2), Base3(num3) { }
  37. // 重写了Base1::foo1()、Base2::foo1()、Base3::foo1()
  38. virtual void foo1() { cout << "Derived4::foo1" << endl;}
  39. virtual void fdd() { cout << "Derived4::fdd" << endl;}
  40. };
  • Derived2继承自Base1、Base2、Base3类,其虚函数表如下所示:
    • Derived继承3个基类,其中为每一个基类都涉及了一张虚函数表
    • 并且将重写的虚函数替换为自己的虚函数指针

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号