• C++虚函数表、地址详解(x86/x64)


    参考博文:c++虚函数表、地址详解-CSDN博客

    本文在上述博文的基础上,补充了x64下的验证代码。

    一.什么是虚函数表,它有什么特点?

            虚函数大家都知道是基本用于实现多态的,当父类指针指向子类对象的时候,如何确定调用的函数是父类里的还是子类里面的,这就要用到虚函数表。下面一点点表诉什么是虚函数表,和虚函数表的特点。如有说错的地方,欢迎指正:

    1.编译器会为每个有虚函数的类创建一个虚函数表

            如有类中没有虚函数,那么这个虚函数表就不存在,而不是表中无数据。同时,有虚函数的类都会有自己的虚函数表,一个类不会有另外一个类的虚函数表,包括两个类属于继承关系。

    2.虚函数表会被一个类的所有对象所拥有

            类的每个虚函数成员占据虚函数表的一行,所以说,如果类中有N个虚函数,那么该虚函数表将会有N*8的大小(x64)。并不是每个类对象都会有自己的表。

    3.编译器会将虚函数表指针存放在类对象最前面的位置

            对于类的每个对象,编译器都会为其生成一个透明不可见的指针,这个指针就是虚函数表指针,存放在该对象内存的最前位置。例如:一个类拥有虚函数表,类对象内存布局中前8个字节就是虚函数表的地址(64位)。这个接下来我们会进行测试。

    4.延伸,由第一条可知,子类继承父类,其虚函数表和表函数地址是不一样的。

    5.父类指针指向子类对象时,调用时实际上是根据子类对象的虚函数表进行查找。

    6.如果父类有虚函数,子类没有定义虚函数,那么子类也会有虚函数表。

    二.测试

    1.测试虚函数表地址和虚函数地址
    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. #include <iostream>
    4. #ifdef _WIN64
    5. #define PTR_VAL int64_t
    6. #else
    7. #define PTR_VAL int32_t
    8. #endif
    9. using namespace std;
    10. class Father
    11. {
    12. virtual void FatherFun1() { cout << "FatherFun1" << endl; }
    13. virtual void FatherFun2() { cout << "FatherFun2" << endl; }
    14. virtual void FatherFun3() { cout << "FatherFun3" << endl; }
    15. };
    16. typedef void (*Fun)(void);
    17. int main()
    18. {
    19. Father father;
    20. cout << "类对象地址:" << &father << endl;
    21. cout << "虚函数表地址: " << (PTR_VAL*)*(PTR_VAL*)&father << endl;
    22. cout << "虚函数FatherFun1地址:" << (PTR_VAL*)*(PTR_VAL*)&father << endl;
    23. cout << "虚函数FatherFun2地址:" << (PTR_VAL*)*(PTR_VAL*)&father + 1 << endl;
    24. cout << "虚函数FatherFun3地址:" << (PTR_VAL*)*(PTR_VAL*)&father + 2 << endl;
    25. cout << "测试地址是否正确" << endl;
    26. Fun fun = (Fun) * (PTR_VAL*)*(PTR_VAL*)&father;
    27. fun();
    28. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&father) + 1);
    29. fun();
    30. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&father) + 2);
    31. fun();
    32. system("pause");
    33. }

             上面说过, x64下,类对象内存中前8个字节就是虚函数表的地址,那么我们获取类对象前8个字节,(int64_t)&father就是虚函数表地址,我们再给他转换成地址指针为(int64_t*)(int64_t)&father。然后我们在看一下输出结果:

    1. 类对象地址:000000ADA0B2F768
    2. 虚函数表地址: 00007FF7FEAABCB0
    3. 虚函数FatherFun1地址:00007FF7FEAABCB0
    4. 虚函数FatherFun2地址:00007FF7FEAABCB8
    5. 虚函数FatherFun3地址:00007FF7FEAABCC0
    6. 测试地址是否正确
    7. FatherFun1
    8. FatherFun2
    9. FatherFun3
    10. 请按任意键继续. . .

            从这个输出我们可以看到,FatherFun1、FatherFun2、FatherFun3的地址只差了8个字节,且输出是正确的,那么第三点已确认

    2.多个类对象的虚函数表地址

    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. #include <iostream>
    4. #ifdef _WIN64
    5. #define PTR_VAL int64_t
    6. #else
    7. #define PTR_VAL int32_t
    8. #endif
    9. using namespace std;
    10. class Father
    11. {
    12. virtual void FatherFun1() { cout << "FatherFun1" << endl; }
    13. virtual void FatherFun2() { cout << "FatherFun2" << endl; }
    14. virtual void FatherFun3() { cout << "FatherFun3" << endl; }
    15. };
    16. typedef void (*Fun)(void);
    17. int main()
    18. {
    19. Father father;
    20. cout << "类对象地址:" << &father << endl;
    21. cout << "虚函数表地址: " << (PTR_VAL*)*(PTR_VAL*)&father << endl;
    22. cout << "虚函数FatherFun1地址:" << (PTR_VAL*)*(PTR_VAL*)&father << endl;
    23. cout << "虚函数FatherFun2地址:" << (PTR_VAL*)*(PTR_VAL*)&father + 1 << endl;
    24. cout << "虚函数FatherFun3地址:" << (PTR_VAL*)*(PTR_VAL*)&father + 2 << endl;
    25. cout << "第二个类对象" << endl;
    26. Father father1;
    27. cout << "类对象地址:" << &father1 << endl;
    28. cout << "虚函数表地址: " << (PTR_VAL*)*(PTR_VAL*)&father1 << endl;
    29. cout << "虚函数FatherFun1地址:" << (PTR_VAL*)*(PTR_VAL*)&father1 << endl;
    30. cout << "虚函数FatherFun2地址:" << (PTR_VAL*)*(PTR_VAL*)&father1 + 1 << endl;
    31. cout << "虚函数FatherFun3地址:" << (PTR_VAL*)*(PTR_VAL*)&father1 + 2 << endl;
    32. system("pause");
    33. }

    定义了两个对象,分别看两个对象的虚函数表地址是否正确,发现两个类对象虚函数表地址相同。

    以下为输出:

    1. 类对象地址:00000070C3DCFC18
    2. 虚函数表地址: 00007FF775FCBCF8
    3. 虚函数FatherFun1地址:00007FF775FCBCF8
    4. 虚函数FatherFun2地址:00007FF775FCBD00
    5. 虚函数FatherFun3地址:00007FF775FCBD08
    6. 第二个类对象
    7. 类对象地址:00000070C3DCFC38
    8. 虚函数表地址: 00007FF775FCBCF8
    9. 虚函数FatherFun1地址:00007FF775FCBCF8
    10. 虚函数FatherFun2地址:00007FF775FCBD00
    11. 虚函数FatherFun3地址:00007FF775FCBD08
    12. 请按任意键继续. . .

    由此可见,第二点现在已经确认

    3.测试类中无虚函数,是否存在虚函数表

    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. #include <iostream>
    4. #ifdef _WIN64
    5. #define PTR_VAL int64_t
    6. #else
    7. #define PTR_VAL int32_t
    8. #endif
    9. using namespace std;
    10. class Test
    11. {
    12. void TestFun1() { cout << "TestFun1" << endl; }
    13. void TestFun2() { cout << "TestFun2" << endl; }
    14. void TestFun3() { cout << "TestFun3" << endl; }
    15. };
    16. typedef void (*Fun)(void);
    17. int main()
    18. {
    19. Test test;
    20. cout << "类对象地址:" << &test << endl;
    21. cout << "虚函数表地址: " << (PTR_VAL*)*(PTR_VAL*)&test << endl;
    22. cout << "虚函数TestFun1地址:" << (PTR_VAL*)*(PTR_VAL*)&test << endl;
    23. cout << "虚函数TestFun2地址:" << (PTR_VAL*)*(PTR_VAL*)&test + 1 << endl;
    24. cout << "虚函数TestFun3地址:" << (PTR_VAL*)*(PTR_VAL*)&test + 2 << endl;
    25. system("pause");
    26. }

    以下为输出:

    1. 类对象地址:000000AEBF9DFB44
    2. 虚函数表地址: CCCCCCCCCCCCCCCC
    3. 虚函数TestFun1地址:CCCCCCCCCCCCCCCC
    4. 虚函数TestFun2地址:CCCCCCCCCCCCCCD4
    5. 虚函数TestFun3地址:CCCCCCCCCCCCCCDC
    6. 请按任意键继续. . .

    由此可见虚函数表地址为错误,第一条确认

    4.子类继承父类,但不继承虚函数表

    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. #include <iostream>
    4. #ifdef _WIN64
    5. #define PTR_VAL int64_t
    6. #else
    7. #define PTR_VAL int32_t
    8. #endif
    9. using namespace std;
    10. class Father
    11. {
    12. virtual void FatherFun1() { cout << "FatherFun1" << endl; }
    13. virtual void FatherFun2() { cout << "FatherFun2" << endl; }
    14. virtual void FatherFun3() { cout << "FatherFun3" << endl; }
    15. };
    16. class Son : public Father
    17. {
    18. virtual void SonFun1() { cout << "SonFun1" << endl; }
    19. virtual void SonFun2() { cout << "SonFun2" << endl; }
    20. virtual void SonFun3() { cout << "SonFun3" << endl; }
    21. };
    22. typedef void (*Fun)(void);
    23. int main()
    24. {
    25. Father father;
    26. cout << "类对象地址:" << &father << endl;
    27. cout << "虚函数表地址: " << (PTR_VAL*)*(PTR_VAL*)&father << endl;
    28. cout << "虚函数FatherFun1地址:" << (PTR_VAL*)*(PTR_VAL*)&father << endl;
    29. cout << "虚函数FatherFun2地址:" << (PTR_VAL*)*(PTR_VAL*)&father + 1 << endl;
    30. cout << "虚函数FatherFun3地址:" << (PTR_VAL*)*(PTR_VAL*)&father + 2 << endl;
    31. //cout << "虚函数sonFun1地址:" <<(PTR_VAL*)*(PTR_VAL*)&father + 3 << endl;
    32. //cout << "虚函数sonFun2地址:" <<(PTR_VAL*)*(PTR_VAL*)&father + 4 << endl;
    33. //cout << "虚函数sonFun3地址:" <<(PTR_VAL*)*(PTR_VAL*)&father + 5 << endl;
    34. Fun fun = (Fun) * (PTR_VAL*)*(PTR_VAL*)&father;
    35. fun();
    36. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&father) + 1);
    37. fun();
    38. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&father) + 2);
    39. fun();
    40. //fun = (Fun)*((PTR_VAL*)*(PTR_VAL*)(&father) + 3);
    41. //fun();
    42. //fun = (Fun)*((PTR_VAL*)*(PTR_VAL*)(&father) + 4);
    43. //fun();
    44. //fun = (Fun)*((PTR_VAL*)*(PTR_VAL*)(&father) + 5);
    45. //fun();
    46. cout << "----------------测试子类------------------" << endl;
    47. Son son;
    48. cout << "类对象地址:" << &son << endl;
    49. cout << "虚函数表地址: " << (PTR_VAL*)*(PTR_VAL*)&son << endl;
    50. cout << "虚函数继承FatherFun1地址:" << (PTR_VAL*)*(PTR_VAL*)&son << endl;
    51. cout << "虚函数继承FatherFun2地址:" << (PTR_VAL*)*(PTR_VAL*)&son + 1 << endl;
    52. cout << "虚函数继承FatherFun3地址:" << (PTR_VAL*)*(PTR_VAL*)&son + 2 << endl;
    53. cout << "虚函数sonFun1地址:" << (PTR_VAL*)*(PTR_VAL*)&son + 3 << endl;
    54. cout << "虚函数sonFun2地址:" << (PTR_VAL*)*(PTR_VAL*)&son + 4 << endl;
    55. cout << "虚函数sonFun3地址:" << (PTR_VAL*)*(PTR_VAL*)&son + 5 << endl;
    56. fun = (Fun) * (PTR_VAL*)*(PTR_VAL*)&son;
    57. fun();
    58. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&son) + 1);
    59. fun();
    60. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&son) + 2);
    61. fun();
    62. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&son) + 3);
    63. fun();
    64. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&son) + 4);
    65. fun();
    66. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&son) + 5);
    67. fun();
    68. system("pause");
    69. }

    如果将代码中注释代码给恢复,会直接发生段错误。

    以下为输出结果:

    1. 类对象地址:000000A9936FF9A8
    2. 虚函数表地址: 00007FF667CABCF8
    3. 虚函数FatherFun1地址:00007FF667CABCF8
    4. 虚函数FatherFun2地址:00007FF667CABD00
    5. 虚函数FatherFun3地址:00007FF667CABD08
    6. FatherFun1
    7. FatherFun2
    8. FatherFun3
    9. ----------------测试子类------------------
    10. 类对象地址:000000A9936FF9E8
    11. 虚函数表地址: 00007FF667CABD50
    12. 虚函数继承FatherFun1地址:00007FF667CABD50
    13. 虚函数继承FatherFun2地址:00007FF667CABD58
    14. 虚函数继承FatherFun3地址:00007FF667CABD60
    15. 虚函数sonFun1地址:00007FF667CABD68
    16. 虚函数sonFun2地址:00007FF667CABD70
    17. 虚函数sonFun3地址:00007FF667CABD78
    18. FatherFun1
    19. FatherFun2
    20. FatherFun3
    21. SonFun1
    22. SonFun2
    23. SonFun3
    24. 请按任意键继续. . .

            由输出结果我们可以看到,父类和子类中的虚函数表地址是不一样的,而且子类继承了父类的虚函数,但是其地址是和父类中不一样的!。第四点确认!
            并且我们根据输出可以发现,子类虚函数表中虚函数地址排序为先是继承的父类的函数,再是子类中的虚函数。

    5.子类重写父类中虚函数,或子类重定义父类虚函数

    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. #include <iostream>
    4. #ifdef _WIN64
    5. #define PTR_VAL int64_t
    6. #else
    7. #define PTR_VAL int32_t
    8. #endif
    9. using namespace std;
    10. class Father
    11. {
    12. virtual void FatherFun4() { cout << "FatherFun4" << endl; }
    13. virtual void FatherFun1() { cout << "FatherFun1" << endl; }
    14. virtual void FatherFun2() { cout << "FatherFun2" << endl; }
    15. virtual void FatherFun3() { cout << "FatherFun3" << endl; }
    16. };
    17. class Son : public Father
    18. {
    19. virtual void SonFun1() { cout << "SonFun1" << endl; }
    20. virtual void SonFun2() { cout << "SonFun2" << endl; }
    21. virtual void SonFun3() { cout << "SonFun3" << endl; }
    22. virtual void FatherFun4() { cout << "SonGetFatherFun4" << endl; }
    23. virtual void FatherFun4(int a = 0) { cout << "SonGetFatherFun4 diffrent param" << endl; }
    24. };
    25. typedef void (*Fun)(void);
    26. typedef void (*FunParam)(int);
    27. int main()
    28. {
    29. #if 1
    30. Father father;
    31. cout << "类对象地址:" << &father << endl;
    32. cout << "虚函数表地址: " << (PTR_VAL*)*(PTR_VAL*)&father << endl;
    33. cout << "虚函数FatherFun4地址:" << (PTR_VAL*)*(PTR_VAL*)&father << endl;
    34. cout << "虚函数FatherFun1地址:" << (PTR_VAL*)*(PTR_VAL*)&father + 1 << endl;
    35. cout << "虚函数FatherFun2地址:" << (PTR_VAL*)*(PTR_VAL*)&father + 2 << endl;
    36. cout << "虚函数FatherFun3地址:" << (PTR_VAL*)*(PTR_VAL*)&father + 3 << endl;
    37. Fun fun = (Fun) * (PTR_VAL*)*(PTR_VAL*)&father;
    38. fun();
    39. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&father) + 1);
    40. fun();
    41. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&father) + 2);
    42. fun();
    43. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&father) + 3);
    44. fun();
    45. cout << "----------------测试子类------------------" << endl;
    46. Son son;
    47. cout << "类对象地址:" << &son << endl;
    48. cout << "虚函数表地址: " << (PTR_VAL*)*(PTR_VAL*)&son << endl;
    49. cout << "虚函数继承SonGetFatherFun4地址:" << (PTR_VAL*)*(PTR_VAL*)&son << endl;
    50. cout << "虚函数继承FatherFun1地址:" << (PTR_VAL*)*(PTR_VAL*)&son + 1 << endl;
    51. cout << "虚函数继承FatherFun2地址:" << (PTR_VAL*)*(PTR_VAL*)&son + 2 << endl;
    52. cout << "虚函数继承FatherFun3地址:" << (PTR_VAL*)*(PTR_VAL*)&son + 3 << endl;
    53. cout << "虚函数sonFun1地址:" << (PTR_VAL*)*(PTR_VAL*)&son + 4 << endl;
    54. cout << "虚函数sonFun2地址:" << (PTR_VAL*)*(PTR_VAL*)&son + 5 << endl;
    55. cout << "虚函数sonFun3地址:" << (PTR_VAL*)*(PTR_VAL*)&son + 6 << endl;
    56. cout << "虚函数SonGetFatherFun4 diffrent param地址:" << (PTR_VAL*)*(PTR_VAL*)&son + 7 << endl;
    57. fun = (Fun) * (PTR_VAL*)*(PTR_VAL*)&son;
    58. fun();
    59. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&son) + 1);
    60. fun();
    61. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&son) + 2);
    62. fun();
    63. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&son) + 3);
    64. fun();
    65. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&son) + 4);
    66. fun();
    67. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&son) + 5);
    68. fun();
    69. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&son) + 6);
    70. fun();
    71. FunParam funParam = (FunParam) * ((PTR_VAL*)*(PTR_VAL*)(&son) + 7);
    72. funParam(0);
    73. #endif
    74. system("pause");
    75. }

            我们故意将父类中FatherFun4放在最前,而子类中的重写和重定义的FatherFun4 函数放在最后,看一下虚函数表中虚函数排放顺序是否有发生改变。

    以下为输出结果:

    1. 虚函数FatherFun1地址:00007FF76199BD00
    2. 虚函数FatherFun2地址:00007FF76199BD08
    3. 虚函数FatherFun3地址:00007FF76199BD10
    4. FatherFun4
    5. FatherFun1
    6. FatherFun2
    7. FatherFun3
    8. ----------------测试子类------------------
    9. 类对象地址:0000003360F5F648
    10. 虚函数表地址: 00007FF76199BD68
    11. 虚函数继承SonGetFatherFun4地址:00007FF76199BD68
    12. 虚函数继承FatherFun1地址:00007FF76199BD70
    13. 虚函数继承FatherFun2地址:00007FF76199BD78
    14. 虚函数继承FatherFun3地址:00007FF76199BD80
    15. 虚函数sonFun1地址:00007FF76199BD88
    16. 虚函数sonFun2地址:00007FF76199BD90
    17. 虚函数sonFun3地址:00007FF76199BD98
    18. 虚函数SonGetFatherFun4 diffrent param地址:00007FF76199BDA0
    19. SonGetFatherFun4
    20. FatherFun1
    21. FatherFun2
    22. FatherFun3
    23. SonFun1
    24. SonFun2
    25. SonFun3
    26. SonGetFatherFun4 diffrent param
    27. 请按任意键继续. . .

            我们可以清楚的看到,位于子类最后函数重写的FatherFun4被放在了本该继承的父类FatherFun4位置上(先输出的是SonGetFatherFun4),而函数重定义则没有发生改变。

    由此可以得出结论:

    (1)覆盖的FatherFun4函数被放到了虚函数表中原父类虚函数的位置

    (2)没有被覆盖的函数没有变化

    6.子类指针指向父类

    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. #include <iostream>
    4. #ifdef _WIN64
    5. #define PTR_VAL int64_t
    6. #else
    7. #define PTR_VAL int32_t
    8. #endif
    9. using namespace std;
    10. class Father
    11. {
    12. virtual void FatherFun1() { cout << "FatherFun1" << endl; }
    13. virtual void FatherFun2() { cout << "FatherFun2" << endl; }
    14. virtual void FatherFun3() { cout << "FatherFun3" << endl; }
    15. };
    16. class Son : public Father
    17. {
    18. virtual void SonFun1() { cout << "SonFun1" << endl; }
    19. virtual void SonFun2() { cout << "SonFun2" << endl; }
    20. virtual void SonFun3() { cout << "SonFun3" << endl; }
    21. };
    22. #if 0
    23. class Test
    24. {
    25. void TestFun1() { cout << "TestFun1" << endl; }
    26. void TestFun2() { cout << "TestFun2" << endl; }
    27. void TestFun3() { cout << "TestFun3" << endl; }
    28. };
    29. #endif
    30. typedef void (*Fun)(void);
    31. typedef void (*FunParam)(int);
    32. int main()
    33. {
    34. cout << "----------------测试父类------------------" << endl;
    35. Father father;
    36. cout << "类对象地址:" << &father << endl;
    37. cout << "虚函数表地址: " << (PTR_VAL*)*(PTR_VAL*)&father << endl;
    38. cout << "虚函数FatherFun1地址:" << (PTR_VAL*)*(PTR_VAL*)&father << endl;
    39. cout << "虚函数FatherFun1地址:" << (PTR_VAL*)*(PTR_VAL*)&father + 1 << endl;
    40. cout << "虚函数FatherFun2地址:" << (PTR_VAL*)*(PTR_VAL*)&father + 2 << endl;
    41. Fun fun = (Fun) * (PTR_VAL*)*(PTR_VAL*)&father;
    42. fun();
    43. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&father) + 1);
    44. fun();
    45. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&father) + 2);
    46. fun();
    47. cout << "----------------测试子类------------------" << endl;
    48. Son son;
    49. cout << "类对象地址:" << &son << endl;
    50. cout << "虚函数表地址: " << (PTR_VAL*)*(PTR_VAL*)&son << endl;
    51. cout << "虚函数继承FatherFun1地址:" << (PTR_VAL*)*(PTR_VAL*)&son << endl;
    52. cout << "虚函数继承FatherFun2地址:" << (PTR_VAL*)*(PTR_VAL*)&son + 1 << endl;
    53. cout << "虚函数继承FatherFun3地址:" << (PTR_VAL*)*(PTR_VAL*)&son + 2 << endl;
    54. cout << "虚函数sonFun1地址:" << (PTR_VAL*)*(PTR_VAL*)&son + 3 << endl;
    55. cout << "虚函数sonFun2地址:" << (PTR_VAL*)*(PTR_VAL*)&son + 4 << endl;
    56. cout << "虚函数sonFun3地址:" << (PTR_VAL*)*(PTR_VAL*)&son + 5 << endl;
    57. fun = (Fun) * (PTR_VAL*)*(PTR_VAL*)&son;
    58. fun();
    59. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&son) + 1);
    60. fun();
    61. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&son) + 2);
    62. fun();
    63. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&son) + 3);
    64. fun();
    65. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&son) + 4);
    66. fun();
    67. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&son) + 5);
    68. fun();
    69. cout << "----------------测试父类指针指向子类------------------" << endl;
    70. Father* pointSon = new Son;
    71. cout << "类对象地址:" << pointSon << endl;
    72. cout << "虚函数表地址: " << (PTR_VAL*)*(PTR_VAL*)pointSon << endl;
    73. cout << "虚函数继承FatherFun1地址:" << (PTR_VAL*)*(PTR_VAL*)pointSon << endl;
    74. cout << "虚函数继承FatherFun2地址:" << (PTR_VAL*)*(PTR_VAL*)pointSon + 1 << endl;
    75. cout << "虚函数继承FatherFun3地址:" << (PTR_VAL*)*(PTR_VAL*)pointSon + 2 << endl;
    76. cout << "虚函数sonFun1地址:" << (PTR_VAL*)*(PTR_VAL*)pointSon + 3 << endl;
    77. cout << "虚函数sonFun2地址:" << (PTR_VAL*)*(PTR_VAL*)pointSon + 4 << endl;
    78. cout << "虚函数sonFun3地址:" << (PTR_VAL*)*(PTR_VAL*)pointSon + 5 << endl;
    79. fun = (Fun) * (PTR_VAL*)*(PTR_VAL*)pointSon;
    80. fun();
    81. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(pointSon)+1);
    82. fun();
    83. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(pointSon)+2);
    84. fun();
    85. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(pointSon)+3);
    86. fun();
    87. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(pointSon)+4);
    88. fun();
    89. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(pointSon)+5);
    90. fun();
    91. system("pause");
    92. }

    实际上父类指针指向子类,调用的是子类的虚函数表。

    以下为输出:

    1. 类对象地址:00000058117AF688
    2. 虚函数表地址: 00007FF6F469CD50
    3. 虚函数继承FatherFun1地址:00007FF6F469CD50
    4. 虚函数继承FatherFun2地址:00007FF6F469CD58
    5. 虚函数继承FatherFun3地址:00007FF6F469CD60
    6. 虚函数sonFun1地址:00007FF6F469CD68
    7. 虚函数sonFun2地址:00007FF6F469CD70
    8. 虚函数sonFun3地址:00007FF6F469CD78
    9. FatherFun1
    10. FatherFun2
    11. FatherFun3
    12. SonFun1
    13. SonFun2
    14. SonFun3
    15. ----------------测试父类指针指向子类------------------
    16. 类对象地址:0000021C918185C0
    17. 虚函数表地址: 00007FF6F469CD50
    18. 虚函数继承FatherFun1地址:00007FF6F469CD50
    19. 虚函数继承FatherFun2地址:00007FF6F469CD58
    20. 虚函数继承FatherFun3地址:00007FF6F469CD60
    21. 虚函数sonFun1地址:00007FF6F469CD68
    22. 虚函数sonFun2地址:00007FF6F469CD70
    23. 虚函数sonFun3地址:00007FF6F469CD78
    24. FatherFun1
    25. FatherFun2
    26. FatherFun3
    27. SonFun1
    28. SonFun2
    29. SonFun3
    30. 请按任意键继续. . .

            由虚函数表地址我们就可以看到,父类指针指向子类的时候,其虚函数表地址和子类虚函数表地址是一样的。由此,第五点确认。

    7.子类重写父类虚函数,并父类指针指向子类

    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. #include <iostream>
    4. #ifdef _WIN64
    5. #define PTR_VAL int64_t
    6. #else
    7. #define PTR_VAL int32_t
    8. #endif
    9. using namespace std;
    10. class Father
    11. {
    12. virtual void FatherFun4() { cout << "FatherFun4" << endl; }
    13. virtual void FatherFun1() { cout << "FatherFun1" << endl; }
    14. virtual void FatherFun2() { cout << "FatherFun2" << endl; }
    15. virtual void FatherFun3() { cout << "FatherFun3" << endl; }
    16. };
    17. class Son : public Father
    18. {
    19. virtual void SonFun1() { cout << "SonFun1" << endl; }
    20. virtual void SonFun2() { cout << "SonFun2" << endl; }
    21. virtual void SonFun3() { cout << "SonFun3" << endl; }
    22. virtual void FatherFun4() { cout << "SonGetFatherFun4" << endl; }
    23. };
    24. #if 0
    25. class Test
    26. {
    27. void TestFun1() { cout << "TestFun1" << endl; }
    28. void TestFun2() { cout << "TestFun2" << endl; }
    29. void TestFun3() { cout << "TestFun3" << endl; }
    30. };
    31. #endif
    32. typedef void (*Fun)(void);
    33. typedef void (*FunParam)(int);
    34. int main()
    35. {
    36. cout << "----------------测试父类------------------" << endl;
    37. Father father;
    38. cout << "类对象地址:" << &father << endl;
    39. cout << "虚函数表地址: " << (PTR_VAL*)*(PTR_VAL*)&father << endl;
    40. cout << "虚函数FatherFun4地址:" << (PTR_VAL*)*(PTR_VAL*)&father << endl;
    41. cout << "虚函数FatherFun1地址:" << (PTR_VAL*)*(PTR_VAL*)&father + 1 << endl;
    42. cout << "虚函数FatherFun2地址:" << (PTR_VAL*)*(PTR_VAL*)&father + 2 << endl;
    43. cout << "虚函数FatherFun3地址:" << (PTR_VAL*)*(PTR_VAL*)&father + 3 << endl;
    44. Fun fun = (Fun) * (PTR_VAL*)*(PTR_VAL*)&father;
    45. fun();
    46. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&father) + 1);
    47. fun();
    48. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&father) + 2);
    49. fun();
    50. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&father) + 3);
    51. fun();
    52. cout << "----------------测试子类------------------" << endl;
    53. Son son;
    54. cout << "类对象地址:" << &son << endl;
    55. cout << "虚函数表地址: " << (PTR_VAL*)*(PTR_VAL*)&son << endl;
    56. cout << "虚函数继承FatherFun4地址:" << (PTR_VAL*)*(PTR_VAL*)&son << endl;
    57. cout << "虚函数继承FatherFun1地址:" << (PTR_VAL*)*(PTR_VAL*)&son + 1 << endl;
    58. cout << "虚函数继承FatherFun2地址:" << (PTR_VAL*)*(PTR_VAL*)&son + 2 << endl;
    59. cout << "虚函数继承FatherFun3地址:" << (PTR_VAL*)*(PTR_VAL*)&son + 3 << endl;
    60. cout << "虚函数sonFun1地址:" << (PTR_VAL*)*(PTR_VAL*)&son + 4 << endl;
    61. cout << "虚函数sonFun2地址:" << (PTR_VAL*)*(PTR_VAL*)&son + 5 << endl;
    62. cout << "虚函数sonFun3地址:" << (PTR_VAL*)*(PTR_VAL*)&son + 6 << endl;
    63. fun = (Fun) * (PTR_VAL*)*(PTR_VAL*)&son;
    64. fun();
    65. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&son) + 1);
    66. fun();
    67. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&son) + 2);
    68. fun();
    69. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&son) + 3);
    70. fun();
    71. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&son) + 4);
    72. fun();
    73. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&son) + 5);
    74. fun();
    75. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&son) + 6);
    76. fun();
    77. cout << "----------------测试父类指针指向子类------------------" << endl;
    78. Father* pointSon = new Son;
    79. cout << "类对象地址:" << pointSon << endl;
    80. cout << "虚函数表地址: " << (PTR_VAL*)*(PTR_VAL*)pointSon << endl;
    81. cout << "虚函数继承FatherFun1地址:" << (PTR_VAL*)*(PTR_VAL*)pointSon << endl;
    82. cout << "虚函数继承FatherFun2地址:" << (PTR_VAL*)*(PTR_VAL*)pointSon + 1 << endl;
    83. cout << "虚函数继承FatherFun3地址:" << (PTR_VAL*)*(PTR_VAL*)pointSon + 2 << endl;
    84. cout << "虚函数sonFun1地址:" << (PTR_VAL*)*(PTR_VAL*)pointSon + 3 << endl;
    85. cout << "虚函数sonFun2地址:" << (PTR_VAL*)*(PTR_VAL*)pointSon + 4 << endl;
    86. cout << "虚函数sonFun3地址:" << (PTR_VAL*)*(PTR_VAL*)pointSon + 5 << endl;
    87. fun = (Fun) * (PTR_VAL*)*(PTR_VAL*)pointSon;
    88. fun();
    89. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(pointSon)+1);
    90. fun();
    91. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(pointSon)+2);
    92. fun();
    93. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(pointSon)+3);
    94. fun();
    95. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(pointSon)+4);
    96. fun();
    97. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(pointSon)+5);
    98. fun();
    99. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(pointSon)+6);
    100. fun();
    101. system("pause");
    102. }

    直接看输出结果:

    1. ----------------测试父类------------------
    2. 类对象地址:000000555C70FBF8
    3. 虚函数表地址: 00007FF61736CCF8
    4. 虚函数FatherFun4地址:00007FF61736CCF8
    5. 虚函数FatherFun1地址:00007FF61736CD00
    6. 虚函数FatherFun2地址:00007FF61736CD08
    7. 虚函数FatherFun3地址:00007FF61736CD10
    8. FatherFun4
    9. FatherFun1
    10. FatherFun2
    11. FatherFun3
    12. ----------------测试子类------------------
    13. 类对象地址:000000555C70FC38
    14. 虚函数表地址: 00007FF61736CD68
    15. 虚函数继承FatherFun4地址:00007FF61736CD68
    16. 虚函数继承FatherFun1地址:00007FF61736CD70
    17. 虚函数继承FatherFun2地址:00007FF61736CD78
    18. 虚函数继承FatherFun3地址:00007FF61736CD80
    19. 虚函数sonFun1地址:00007FF61736CD88
    20. 虚函数sonFun2地址:00007FF61736CD90
    21. 虚函数sonFun3地址:00007FF61736CD98
    22. SonGetFatherFun4
    23. FatherFun1
    24. FatherFun2
    25. FatherFun3
    26. SonFun1
    27. SonFun2
    28. SonFun3
    29. ----------------测试父类指针指向子类------------------
    30. 类对象地址:0000019CE3F99140
    31. 虚函数表地址: 00007FF61736CD68
    32. 虚函数继承FatherFun1地址:00007FF61736CD68
    33. 虚函数继承FatherFun2地址:00007FF61736CD70
    34. 虚函数继承FatherFun3地址:00007FF61736CD78
    35. 虚函数sonFun1地址:00007FF61736CD80
    36. 虚函数sonFun2地址:00007FF61736CD88
    37. 虚函数sonFun3地址:00007FF61736CD90
    38. SonGetFatherFun4
    39. FatherFun1
    40. FatherFun2
    41. FatherFun3
    42. SonFun1
    43. SonFun2
    44. SonFun3
    45. 请按任意键继续. . .

    实际上和第六点结果是一样的。

    8.如果父类有虚函数,子类没有定义虚函数,子类也会有虚函数表

    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. #include <iostream>
    4. #ifdef _WIN64
    5. #define PTR_VAL int64_t
    6. #else
    7. #define PTR_VAL int32_t
    8. #endif
    9. using namespace std;
    10. class Father
    11. {
    12. virtual void FatherFun1() { cout << "FatherFun1" << endl; }
    13. virtual void FatherFun2() { cout << "FatherFun2" << endl; }
    14. virtual void FatherFun3() { cout << "FatherFun3" << endl; }
    15. };
    16. class Son : public Father
    17. {
    18. //virtual void SonFun1() { cout << "SonFun1" << endl; }
    19. //virtual void SonFun2() { cout << "SonFun2" << endl; }
    20. //virtual void SonFun3() { cout << "SonFun3" << endl; }
    21. };
    22. typedef void (*Fun)(void);
    23. int main()
    24. {
    25. Father father;
    26. cout << "类对象地址:" << &father << endl;
    27. cout << "虚函数表地址: " << (PTR_VAL*)*(PTR_VAL*)&father << endl;
    28. cout << "虚函数FatherFun1地址:" << (PTR_VAL*)*(PTR_VAL*)&father << endl;
    29. cout << "虚函数FatherFun2地址:" << (PTR_VAL*)*(PTR_VAL*)&father + 1 << endl;
    30. cout << "虚函数FatherFun3地址:" << (PTR_VAL*)*(PTR_VAL*)&father + 2 << endl;
    31. Fun fun = (Fun) * (PTR_VAL*)*(PTR_VAL*)&father;
    32. fun();
    33. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&father) + 1);
    34. fun();
    35. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&father) + 2);
    36. fun();
    37. cout << "----------------测试子类------------------" << endl;
    38. Son son;
    39. cout << "类对象地址:" << &son << endl;
    40. cout << "虚函数表地址: " << (PTR_VAL*)*(PTR_VAL*)&son << endl;
    41. cout << "虚函数继承FatherFun1地址:" << (PTR_VAL*)*(PTR_VAL*)&son << endl;
    42. cout << "虚函数继承FatherFun2地址:" << (PTR_VAL*)*(PTR_VAL*)&son + 1 << endl;
    43. cout << "虚函数继承FatherFun3地址:" << (PTR_VAL*)*(PTR_VAL*)&son + 2 << endl;
    44. //cout << "虚函数sonFun1地址:" << (PTR_VAL*)*(PTR_VAL*)&son + 3 << endl;
    45. //cout << "虚函数sonFun2地址:" << (PTR_VAL*)*(PTR_VAL*)&son + 4 << endl;
    46. //cout << "虚函数sonFun3地址:" << (PTR_VAL*)*(PTR_VAL*)&son + 5 << endl;
    47. fun = (Fun) * (PTR_VAL*)*(PTR_VAL*)&son;
    48. fun();
    49. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&son) + 1);
    50. fun();
    51. fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&son) + 2);
    52. fun();
    53. //fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&son) + 3);
    54. //fun();
    55. //fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&son) + 4);
    56. //fun();
    57. //fun = (Fun) * ((PTR_VAL*)*(PTR_VAL*)(&son) + 5);
    58. //fun();
    59. system("pause");
    60. }

            将子类中的虚函数注释掉,输出结果:

    1. 类对象地址:00000097B6CFF958
    2. 虚函数表地址: 00007FF6E459BCF8
    3. 虚函数FatherFun1地址:00007FF6E459BCF8
    4. 虚函数FatherFun2地址:00007FF6E459BD00
    5. 虚函数FatherFun3地址:00007FF6E459BD08
    6. FatherFun1
    7. FatherFun2
    8. FatherFun3
    9. ----------------测试子类------------------
    10. 类对象地址:00000097B6CFF998
    11. 虚函数表地址: 00007FF6E459BD50
    12. 虚函数继承FatherFun1地址:00007FF6E459BD50
    13. 虚函数继承FatherFun2地址:00007FF6E459BD58
    14. 虚函数继承FatherFun3地址:00007FF6E459BD60
    15. FatherFun1
    16. FatherFun2
    17. FatherFun3
    18. 请按任意键继续. . .

      第6条确认。

  • 相关阅读:
    MongoBd 离线安装与管理
    【Python】进阶学习:pandas--groupby()用法详解
    Git目录不对,即当前文件夹不对应git仓库
    Java:一个简单的文件上传服务器
    【Python】【源码】SocketIO做一个网络聊天室软件-服务端源码
    一文带你详细了解浏览器安全
    【C++】list的介绍及使用 | 模拟实现list(万字详解)
    【Java SE】继承
    如何使用$APPEALS法,分析用户期待?
    从Opencv之图像直方图源码,探讨高性能计算设计思想
  • 原文地址:https://blog.csdn.net/yuemouren/article/details/141033313