Virtual function table && polymorphic model

Virtual function table && polymorphic model

During virtual inheritance, there will be an offset table pointer in the derived class, which is used to point to the offset table. In polymorphism, if the derived class rewrites the virtual function of the base class, what is the storage location in memory of the virtual function in the base class and the virtual function in the derived class?

Single Inheritance Polymorphic Model

1.class Base
2.{
3.public:
4. virtual void Funtest1()
5.
{
6. cout<<"Base::Funtest1()"<<endl;
7. }
8.
9. virtual void Funtest2()
10.
{
11. cout<<"Base::Funtest2()"<<endl;
12. }
13.
14. int _a;
15.};
16.
17.class Derived: public Base
18.{
19.public:
20. virtual void Funtest1()
21.
{
22. cout<<"Derived::Funtest1()"<<endl;
23. }
24.
25. virtual void Funtest3()
26.
{
27. cout<<"Derived::Funtest2()"<<endl;
28. }
29.
30. int _b;
31.};
32.
33.
34.typedef void (*PVFT)();
35.void Print(Base& b)
36.
{
37. PVFT* pvtf = (PVFT*)(*(int*)&b);
38. while(*pvtf)
39. {
40. (*pvtf)();
41. ++pvtf;
42. }
43.}
44.
45.void Funtest(Base& b)
46.
{
47. //Base b;
48. Derived d;
49. d._a = 10;
50. d._b = 20;
51. b.Funtest1();
52. //Print(d);
53.}
54.
55.int main()
56.
{
57. Base b;
58. Derived d;
59. Funtest(b);
60. Funtest(d);
61. return 0;
62.}
63.

To achieve polymorphism, you must use the pointer or reference of the base class to call the derived class object. 
First, look at the calling order of the base class object and the derived class object (the print function here is determined according to the address of the virtual function in memory. In VS2012, there is a guard interval of 00 00 00 00 behind the virtual function, so you can print according to this feature)


First of all, when the passed actual parameter is a base class object, after calling b.Funtest1() once, it will adjust the Funtest1 in the base class, and then directly print the call of the base class, when the passed actual parameter is an object of the derived class, because the Funtest function is rewritten in the derived class, and then its own Funtest3 function is added, so what is printed is 1 of the derived class, 2 of the base class, and 3 of the derived class

 
Let's take a look at how they are stored in memory. You can know what the virtual function single inheritance model looks like once.

 
This is the address of the base class object, and then enters the memory space pointed to by the pointer in the first row. There are still two addresses stored in this memory space. We don’t know what these two addresses mean. Let’s take a look at the storage of derived class objects in memory.

 
The first thing to store is the assignment of the base class object and the derived class object. You can see that the base class object is placed below, and then enter the space pointed to by the pointer in the first line. After entering, the address is still stored like the base class, but this time it has changed to 3. So we can boldly imagine that the pointer here is actually the address of the virtual function. When it is a base class, there are two virtual functions in the base class, which happen to be two addresses. When it is a derived class, the virtual function of Funtest1 is rewritten, and then there is a Funtest3 in itself, so there are three addresses. The derived class's own virtual functions exist behind the virtual functions of the base class. A pointer like this that points to a virtual function address is called a virtual table pointer, and this space is called a virtual table. 
So you can roughly know the polymorphic model of single inheritance


Multiple Inheritance Polymorphic Model

Let Derived public inherit Base1 and Base2

1.class Base1
2.{
3.public:
4. virtual void Funtest1()
5.
{
6. cout<<"Base1::Funtest1()"<<endl;
7. }
8.
9. virtual void Funtest2()
10.
{
11. cout<<"Base1::Funtest2()"<<endl;
12. }
13.
14. int _a1;
15.};
16.
17.class Base2
18.{
19.public:
20. virtual void Funtest3()
21.
{
22. cout<<"Base2::Funtest3()"<<endl;
23. }
24.
25. virtual void Funtest4()
26.
{
27. cout<<"Base2::Funtest4()"<<endl;
28. }
29.
30. int _a2;
31.};
32.class Derived: public Base1, public Base2
33.{
34.public:
35. virtual void Funtest1()
36.
{
37. cout<<"Derived::Funtest1()"<<endl;
38. }
39.
40. virtual void Funtest3()
41.
{
42. cout<<"Derived::Funtest3()"<<endl;
43. }
44.
45. virtual void Funtest5()
46.
{
47. cout<<"Derived::Funtest5()"<<endl;
48. }
49.
50. virtual void Funtest6()
51.
{
52. cout<<"Derived::Funtest6()"<<endl;
53. }
54. int _b;
55.
56.};
57.
58.
59.typedef void (*PVFT)();
60.void Print(Base1& b)
61.
{
62. PVFT* pvtf = (PVFT*)(*(int*)&b);
63. while(*pvtf)
64. {
65. (*pvtf)();
66. ++pvtf;
67. }
68.}
69.
70.void Funtest(Base1& b)
71.
{
72. //Base b;
73. Derived d;
74. d._a1 = 10;
75. d._a2 = 15;
76. d._b = 20;
77. Print(d);
78.}
79.
80.
81.int main()
82.
{
83. Base1 b1;
84. Base2 b2;
85. Derived d;
86. //Funtest(b2);
87. cout<<endl;
88. Funtest(d);
89. return 0;
90.}

在派生类里将Base1里面的Funtest1重写,将Base2里面的Funtest3重写,然后自己里面添加Funtest5和Funtest6,先分别看看三个类在内存中是怎么存放的。

 
先看派生类,在最下面存放的是派生类里的成员变量的值,往上是0f(base2里面的成员变量的值),然后是一个地址,往上是0a(Base1里面的成员变量的值),再往上就是一个指针。 
进入到Base2处指针指向的空间,里面存放的是2个地址,和单继承相似那这里存放的就是Base2里面虚函数的地址,然后进入最上面的指针指向的地址,里面存放的是4个地址,这4个地址分别是重写Funtest1后的地址,Funtest2的地址,Funtest5的地址和Funtest6的地址。 
而派生类里面的虚函数存在于第一个继承的基类的虚函数后面。 
所以虚函数多继承的一般模型可以描述为


菱形继承多态模型

1.class A
2.{
3.public:
4. virtual void Funtest1()
5.
{
6. cout<<"A::Funtest1()"<<endl;
7. }
8.
9. virtual void Funtest2()
10.
{
11. cout<<"A::Funtest2()"<<endl;
12. }
13.
14. int _a;
15.};
16.
17.class B1: public A
18.{
19.public:
20. virtual void Funtest1()
21.
{
22. cout<<"B1::Funtest1()"<<endl;
23. }
24.
25. virtual void Funtest3()
26.
{
27. cout<<"B1::Funtest3()"<<endl;
28. }
29.
30. int _b1;
31.};
32.
33.class B2: public A
34.{
35.public:
36. virtual void Funtest2()
37.
{
38. cout<<"B2::Funtest2()"<<endl;
39. }
40.
41. virtual void Funtest4()
42.
{
43. cout<<"B2::Funtest4()"<<endl;
44. }
45.
46. int _b2;
47.};
48.
49.class C: public B1, public B2
50.{
51.public:
52. virtual void Funtest3()
53.
{
54. cout<<"C::Funtest3()"<<endl;
55. }
56.
57. virtual void Funtest4()
58.
{
59. cout<<"C::Funtest4()"<<endl;
60. }
61.
62. virtual void Funtest5()
63.
{
64. cout<<"C::Funtest5()"<<endl;
65. }
66.
67. int _c;
68.};
69.
70.void Funtest(B1& b)
71.
{
72. C d;
73. d.B1::_a = 1;
74. d.B2::_a = 2;
75. d._b1 = 3;
76. d._b2 = 4;
77. d._c = 5;
78.}
79.
80.int main()
81.
{
82. A a;
83. B1 b1;
84. B2 b2;
85. C c;
86. Funtest(c);
87. cout<<endl;
88. return 0;
89.}

虚函数的菱形继承和菱形继承很相似,不过给各个继承最前面加上一虚表指针 
先来看看内存中的存放位置,看看和猜想的一样吗


果然和菱形继承时一样,而这里要提一下的就是在继承第一个的基类虚表指针中,将派生类中自己私有的虚函数加在了最后面


菱形虚拟继承多态模型

1.class A
2.{
3.public:
4. virtual void Funtest1()
5.
{
6. cout<<"A::Funtest1()"<<endl;
7. }
8.
9. virtual void Funtest2()
10.
{
11. cout<<"A::Funtest2()"<<endl;
12. }
13.
14. int _a;
15.};
16.
17.class B1:virtual public A
18.{
19.public:
20. virtual void Funtest1()
21.
{
22. cout<<"B1::Funtest1()"<<endl;
23. }
24.
25. virtual void Funtest3()
26.
{
27. cout<<"B1::Funtest3()"<<endl;
28. }
29.
30. int _b1;
31.};
32.
33.class B2:virtual public A
34.{
35.public:
36. virtual void Funtest2()
37.
{
38. cout<<"B2::Funtest2()"<<endl;
39. }
40.
41. virtual void Funtest4()
42.
{
43. cout<<"B2::Funtest4()"<<endl;
44. }
45.
46. int _b2;
47.};
48.
49.class C: public B1, public B2
50.{
51.public:
52. virtual void Funtest3()
53.
{
54. cout<<"C::Funtest3()"<<endl;
55. }
56.
57. virtual void Funtest4()
58.
{
59. cout<<"C::Funtest4()"<<endl;
60. }
61.
62. virtual void Funtest5()
63.
{
64. cout<<"C::Funtest5()"<<endl;
65. }
66.
67. int _c;
68.};
69.
70.void Funtest(B1& b)
71.
{
72. C d;
73. d._a = 1;
74. d._b1 = 3;
75. d._b2 = 4;
76. d._c = 5;
77. cout<<sizeof(d)<<endl;
78.}
79.
80.
81.int main()
82.
{
83. A a;
84. B1 b1;
85. B2 b2;
86. C c;
87. Funtest(c);
88. cout<<endl;
89. return 0;
90.}

在刚刚的虚函数菱形继承上面加上虚拟继承后,还是不是和菱形虚拟继承相似,而偏移量表和虚表是如何存在的,那就继续在内存中查看一下,打开内存窗口


是不是看起来特别复杂,可以把内存中信息化为4部分,分别用红线圈起来。首先,可以知道基类还是在最下面,然后往上依次是派生类,再是两个基类。难点就是这几个指针,从下至上依次进入指针查看一下指针指向内存空间中的内容

 
进去之后里面存放的是两个地址,那么大概就可以知道这个指针变量存放的就是A的虚表,里面的两个地址分别是Funtest1和Funtest2的地址。 
再往上的两个地址分别是0x0031dd7c和0x0031dd58,分别查看这两个指针指向的内容

 
左边对应的是0x0031dd58也就是上面的地址,里面存放的是一个地址,结合菱形继承大概可以知道这里面存放的就是B2里面添加的Funtest4,右边对应的是0x0031dd7c,里面存放的是两个地址,这个看起来是不是很眼熟,没错就是偏移量表,只不过在虚拟继承中第一行存放的是相对自己的偏移量,第二行存放的是相对于他的基类的偏移量。而现在再多态中,fcffffff对应的是-4,而这个地址的位置在虚表的下面,所以偏移量表里的第一行就是相对于他自己虚表的偏移量,第二行是相对于基类虚标的偏移量。


下来就是最上面的两个地址,现在应该可以肯定一个是偏移量,另一个是虚表,不过在虚表中,不仅仅存放了B1中的Funtest3而且还有派生类C中的Funtest5,这在每一个虚函数继承中都可以体现到(派生类中多的虚函数都是在第一个基类的虚函数后面存放)。

 
根据得到的这些信息,可以得到虚函数菱形虚拟继承的一般模型


在虚函数虚拟继承中,如果给派生类加上构造函数,整个派生类的大小会多出4个字节来,在内存中查看到的是多了00 00 00 00


Guess you like

Origin blog.csdn.net/prokgtfy9n18/article/details/76274339