Keywords: virtual function. Virtual table, virtual table pointer, dynamic binding, polymorphic
I. Overview
To implement the polymorphism of C + +, C + + uses a dynamic binding technique.
The core of this technique is the virtual function table (hereinafter referred to as the virtual table). This article describes how virtual function tables implement dynamic binding.
Second, the virtual table of the class
Each class that includes a virtual function includes a virtual table.
We know that when a class (a) inherits a second class (B). Class A inherits the call of the function of Class B. So assuming that a base class includes virtual functions, its inheriting classes can also invoke these virtual functions, in other words, a class inherits the base class that includes the virtual function. Then this class also has its own virtual table.
Let's look at the following code.
Class A includes virtual function vfunc1. Vfunc2. Because Class A includes virtual functions, Class A has a virtual table.
class A {public: virtualvoidvfunc1(); virtualvoid vfunc2(); void func1(); void func2();private: int m_data1, m_data2;};
The virtual table 1 of Class A is seen.
Figure 1: Virtual Table of Class A
A virtual table is a pointer array whose elements are pointers to virtual functions, each of which corresponds to a function pointer of a virtual function. What needs to be pointed out is. A normal function is a non-virtual function. Its invocation does not need to pass through the virtual table, so the elements of the virtual table do not include the function pointers of the normal function.
The entry within the virtual table. That is, the assignment of a virtual function pointer occurs at the compiler's compilation stage. That is, during the compilation phase of the code. The virtual table can be constructed.
Third, virtual table pointer
A virtual table belongs to a class, not to a detailed object, and a class needs only a dummy table. All objects of the same class use the same virtual table.
In order to specify the virtual table of the object. The inside of the object includes a pointer to a virtual table to point to the virtual table that you are using. In order for each object of a class that includes a virtual table to have a virtual table pointer, the compiler adds a pointer to the class, *__vptr. Used to point to a virtual table. Thus, when the object of the class is created, it has the pointer, and the value of the pointer is set on its own initiative to point to the virtual table of the class.
Figure 2: Object with its virtual table
As indicated above, the base class of an inheriting class assumes that the virtual function is included, and that the inheriting class has its own virtual table, so the object of this inheriting class also includes a virtual table pointer, which points to its virtual table.
Four, dynamic binding
In this case, you will be wondering how C + + is using virtual tables and virtual table pointers to implement dynamic binding. Let's look at the following code first.
Class A { Public:Virtual void vfunc1();Virtual voidVFUNC2 ();voidFunc1 ();voidFunc2 ();Private:intM_data1, M_data2;}; Class B: PublicA Public:Virtual void vfunc1();voidFunc1 ();Private:intM_data3;}; Class C: PublicB Public:Virtual void VFUNC2();voidFunc2 ();Private:intM_data1, m_data4;};
Class A is the base class, and Class B inherits Class A. Class C Inherits Class B. Class A, class B, Class C, and its object model, for example, are seen in 3.
Figure 3: Class A, Class B, object model of Class C
Because these three classes have virtual functions, the compiler creates a virtual table for each class, which is the virtual table of Class A (a VTBL). Virtual Table of Class B (b vtbl), virtual Table of Class C (C VTBL). Objects of Class A, Class B, and Class C have a virtual table pointer, *__vptr, that points to the virtual table of the class to which it belongs.
Class A includes two virtual functions, so a vtbl consists of two pointers, pointing to A::vfunc1 () and A::VFUNC2 () respectively.
Class B inherits from Class A. So class B can invoke the function of Class A. But because class B overrides the B::vfunc1 () function, the two pointers to B Vtbl point to B::VFUNC1 () and A::VFUNC2 () respectively.
Class C inherits from Class B, so Class C can invoke the function of Class B, but because Class C overrides the C::vfunc2 () function, the two pointers to C VTBL point to B::vfunc1 () (a function that points to the most recent class of inheritance) and C::VFUNC2 ().
Although Figure 3 looks a bit complicated, just grab the "object's virtual table pointer to the virtual table of its own class, and the pointer in the virtual table will point to its inherited short-term virtual function of a class" that will be able to express the object model of these classes in its own mind.
Calls to non-virtual functions do not pass through a virtual table. Therefore, you do not need pointers in the virtual tables to point to these functions.
Suppose we define an object of Class B. Because Bobject is an object of Class B, Bobject includes a virtual table pointer that points to the virtual table of Class B.
int main() { B bObject;}
Now, we declare a pointer p of Class A to point to the object bobject.
Although P is a base class pointer that can only point to the part of the base class, the virtual table pointer is also part of the base class, so p can access the virtual table pointer to the object bobject. Bobject's virtual table pointer points to the virtual table of Class B, so p can access the B vtbl.
3 of what you see.
int main() { B bObject; *p = & bObject;}
What happens when we use P to invoke the VFUNC1 () function?
int main() { B bObject; *p = & bObject; p->vfunc1();}
When the program executes P->VFUNC1 (), it finds that P is a pointer. And the function that is called is a virtual function, the following steps are followed.
First, according to the virtual table pointer p->__vptr to access the object bobject the corresponding virtual table. Although Pointer P is a base class A * type. However, *__vptr is also part of the base class, so it is possible to access the corresponding virtual table of the object through P->__vptr.
Then, in the virtual table, find the corresponding entry for the function being called. Because the virtual table can be constructed at compile time, it is able to navigate to the corresponding entry in the virtual table based on the function being called.
For a call to P->VFUNC1 (), the first item of B VTBL is the vfunc1 corresponding entry.
Finally, the function pointers found in the virtual table are based on. Call the function. Can be seen from Figure 3. The first item of B VTBL points to b::vfunc1 (). So P->vfunc1 () essentially calls the B::VFUNC1 () function.
What happens if P points to the object of Class A?
int main() { A aObject; *p = &aObject; p->vfunc1();}
When Aobject is created, its virtual table pointer __vptr is set to point to a vtbl, so p->__vptr points to a vtbl. Vfunc1 in a VTBL the entry points to the A::VFUNC1 () function, so p->vfunc1 () essentially calls the A::VFUNC1 () function.
You can use the following expression to represent the steps of the above three calling functions:
(*(p->__vptr)[n])(p)
Can see. By using these virtual function tables. The function is called even if a pointer to the base class is used. It is also possible to achieve a virtual function that correctly invokes the actual object in execution.
The process of calling virtual functions through virtual tables is called dynamic binding, and the phenomenon is called execution-time polymorphism. Dynamic binding differs from the traditional function call, which we call static binding, that is, the invocation of a function can be determined in the compilation phase.
So, when does the dynamic binding of the function be performed? This needs to meet the following three conditions.
- To invoke a function by a pointer
- The pointer upcast upward (the transformation of the inheriting class to the base class is called upcast, about what is upcast. Be able to participate in the reference material of this article)
- The call is a virtual function
Assuming that a function call conforms to the above three conditions, the compiler will compile the function call into a dynamic binding, whose function calls the procedure above through the virtual table mechanism.
V. Summary
Packaging. Inheritance, polymorphism is the three characteristics of object-oriented design, and polymorphism can be said to be the key of object-oriented design. C + + uses virtual function table to realize dynamic binding of virtual function and object. Thus, the cornerstone of C + + object-oriented programming is constructed.
References
- "C + + Primer" Third edition, Chinese version, Pan and other translation
- http://www.learncpp.com/cpp-tutorial/125-the-virtual-table/
- Houtie "Best practice in C + +" video, Geek class, 2015
- Upcasting and Downcasting, http://www.bogotobogo.com/cplusplus/upcasting_downcasting.php
Appendix
Demo Sample Code
Analysis of C + + virtual function table