C ++: virtual function table

Source: Internet
Author: User

C ++: virtual function table
I. Reasons for the article

Where is the virtual function table stored?

Ii. Concepts

The role of virtual functions in C ++ is to implement the polymorphism mechanism. With regard to polymorphism, in short, the pointer of the parent type points to the instance of its subclass, and then calls the member function of the actual subclass through the pointer of the parent class. This technology enables the pointer of the parent class to have multiple forms. This is a generic technology. The so-called generic technology, to put it bluntly, is to try to use the same code to implement a variable algorithm. For example, the template technology, RTTI technology, and virtual function technology either try to achieve resolution at compilation or at runtime.

Anyone familiar with C ++ should know that Virtual functions are implemented through a Virtual Table. V-Table for short. In this table, the primary table is the address table for a class virtual function. This table solves the inheritance and overwrite issues and ensures that it can reflect the actual functions. In this way, the table is allocated to the memory of the instance in instances of classes with virtual functions. Therefore, when we use the parent class pointer to operate a subclass, this virtual function table is very important. Like a map, it specifies the actually called function. Here we will focus on this virtual function table. The C ++ compiler should ensure that: the pointer to the virtual function table exists at the top of the object instance (this is to ensure that the obtained virtual function table has the highest performance-if there are multi-level inheritance or multi-level inheritance ). This means that we can get this virtual function table through the address of the object instance, then we can traverse the function pointer and call the corresponding function.

Class Base {public: virtual void f () {cout <"Base: f" <endl;} virtual void g () {cout <"Base :: g "<endl ;}virtual void h () {cout <" Base: h "<endl ;}}; int main () {// freopen ("input.txt", "r", stdin); typedef void (* Fun) (void); Base B; Fun pFun = NULL; cout <"virtual function table address:" <(int *) (& B) <endl; cout <"virtual function table-first function address: "<(int *) (* (int *) (& B) <endl; // Invoke virtual functions pFun = (Fun) * (int *) * (int *) (& B); // Base: f () pFun (); pFun = (Fun) * (int *) (& B) + 1); // Base: g () pFun (); pFun = (Fun) * (int *) (& B) + 2); // Base: h () pFun (); return 0 ;}

Output in vs2012 environment:

Through this example, we can see that we can forcibly convert & B into int * to obtain the address of the virtual function table. Then, the address of the first virtual function can be obtained again, that is, Base: f (), this is verified in the above program (the int * is forcibly converted to a function pointer ).

The structure of the Base instance virtual table is as follows: <喎?http: www.bkjia.com kf ware vc " target="_blank" class="keylink"> VcD4NCjxwPjxpbWcgYWx0PQ = "here write picture description" src = "http://www.bkjia.com/uploads/allimg/160326/0424242102-1.jpg" title = "\"/>

Note: In the figure above, a node is added at the end of the virtual function table, which is the end node of the virtual function table, just like the string Terminator "/0, it indicates the end of the virtual function table. The value of this ending sign is different in different compilers. In WinXP + VS2003, the value is NULL. In Ubuntu 7.10 + Linux 2.6.22 + GCC 4.1.3, if this value is 1, there will be another virtual function table. If the value is 0, it will be the last virtual function table.

In addition, as long as the virtual function of the parent class is inherited, its sub-class is a virtual function no matter whether it is written or not written to virtual.

class Base {public:    virtual void f() { cout << "Base::f" << endl; }};class Base2 : public Base{public:    void f() { cout << "Base2::f" << endl; }};class Derive : public Base2{public:    void f() { cout << "Derive::f" << endl; }};typedef void(*Fun)(void);int main() {    Base2 *b2 = new Derive;    b2->f();    return 0;}

Output:
Derive: f

Iii. Virtual tables in different situations

It is meaningless that the following virtual functions do not cover the parent class. The reason why there is no coverage is to give a comparison. In comparison, you can better understand the specific internal implementation.

1. General inheritance (no virtual function coverage)

The following table lists the virtual functions of an instance: Derive d;

1) virtual functions are stored in the table in the declared order.

2) the virtual function of the parent class is prior to the virtual function of the subclass.

2. General inheritance (with virtual function overwrite)

For instances of a derived class, the virtual function table is

1) The f () function to be overwritten is placed at the original parent class virtual function in the virtual table.

2) The unoverwritten functions remain.

3. Multi-inheritance (no virtual function coverage)

For the virtual function table in the subclass instance

1) Each parent class has its own virtual table.

2) The member function of the subclass is placed in the table of the first parent class. (The first parent class is determined in the Declaration Order)

4. Multi-inheritance (with virtual function coverage)

Subclass virtual function structure

    Derive d;    Base1 *b1 = &d;    Base2 *b2 = &d;    Base3 *b3 = &d;    b1->f(); //Derive::f()    b2->f(); //Derive::f()    b3->f(); //Derive::f()    b1->g(); //Base1::g()    b2->g(); //Base2::g()    b3->g(); //Base3::g()
Iv. virtual function table Security 1. Access the virtual function of the subclass through the parent pointer

We know that it is meaningless for a subclass to not overwrite the virtual function of the parent class. Although we can see in the figure above that the virtual table of Base1 has a virtual function of Derive, we cannot use the following statement to call its own virtual function of the subclass:

Base1 * b1 = new Derive (); b1-> f1 (); // compilation Error

Any attempt to use a parent class pointer to call a member function that does not overwrite the parent class in the subclass will be deemed invalid by the compiler.Therefore, such a program cannot be compiled. However, during runtime, we can access the virtual function table through pointers to violate the C ++ semantics.

The following example can be used.

The following is a routine for accessing multiple inherited virtual function tables:

#include 
  
   using namespace std;class Base1 {public:            virtual void f() { cout << "Base1::f" << endl; }            virtual void g() { cout << "Base1::g" << endl; }            virtual void h() { cout << "Base1::h" << endl; }};class Base2 {public:            virtual void f() { cout << "Base2::f" << endl; }            virtual void g() { cout << "Base2::g" << endl; }            virtual void h() { cout << "Base2::h" << endl; }};class Base3 {public:            virtual void f() { cout << "Base3::f" << endl; }            virtual void g() { cout << "Base3::g" << endl; }            virtual void h() { cout << "Base3::h" << endl; }};class Derive : public Base1, public Base2, public Base3 {public:            virtual void f() { cout << "Derive::f" << endl; }            virtual void g1() { cout << "Derive::g1" << endl; }};typedef void(*Fun)(void);int main(){            Fun pFun = NULL;            Derive d;            int** pVtab = (int**)&d;            //Base1's vtable            //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+0);            pFun = (Fun)pVtab[0][0];            pFun();            //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+1);            pFun = (Fun)pVtab[0][1];            pFun();            //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+2);            pFun = (Fun)pVtab[0][2];            pFun();            //Derive's vtable            //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+3);            pFun = (Fun)pVtab[0][3];            pFun();            //The tail of the vtable            pFun = (Fun)pVtab[0][4];            cout<
   <<
  
2. Access non-public virtual functions

In addition, if the parent class's virtual functions are private or protected, but these non-public virtual functions will also exist in the virtual function table, we can also access these non-public virtual functions by accessing the virtual function table, which is easy to achieve.

For example:

class Base {    private:            virtual void f() { cout << "Base::f" << endl; }};class Derive : public Base{};typedef void(*Fun)(void);void main() {    Derive d;    Fun  pFun = (Fun)*((int*)*(int*)(&d)+0);    pFun();}
VC

We can expand the class instance in the Debug state of the vc ide environment to see the virtual function table (not very complete)

Appendix: Basic Review 1. Polymorphism Concept:

Polymorphism refers to the same message.Completely different behaviors caused by receiving different objects of the class. The message mentioned here isClass member functions. The essence of polymorphism isA function name.

C ++ has two types of polymorphism:

(1) During compilation, polymorphism is implemented throughStatic AssociationImplemented;

(2) The Runtime polymorphism is throughDynamic Association.

Function Association:

When a function is called, it is determined to connect it to the code of the corresponding function body during compilation or running. In essence, it refersStorage address(Function code segment entry) the process of association.

2. Virtual Functions

In C ++Dynamic AssociationYesVirtual FunctionsImplemented. virtual functions must exist in the inherited environment.

When the member functions of a class are described as virtual functions, you can define their basic virtual functions in the (direct or indirect) Derived classes of the class.Same prototype(Note that the function prototype must be identical; otherwise, the virtual function will be hidden.

A virtual function can be redefined in one or more Derived classes. When a new definition is required in a derived classIdentical prototype, IncludingFunction Name, return type, number of parameters, and order of parameter types. In this case, whether or not the keyword virtual is added before the corresponding member function of the derived class is regarded as a virtual function. If the function prototype is different, the function name is the same, C ++ will regard it as a general function overload, rather than a virtual function. Only member functions of a class can be declared as virtual functions. Global functions and static member functions cannot be declared as virtual functions.

3. Pure virtual functions

A pure virtual function declares a virtual function in the base class without providing a specific function definition body. It is placed in a derived class and called a pure virtual function.

The pointer or reference of the base class can call the virtual functions of all derived classes. The base class is only used for inheritance and serves as only one interface. The specific functions are implemented in the derived class.

The declaration of pure virtual functions is as follows: (Note: it should be placed in the definition body of the base class)

Virtual function prototype = 0;

Note:

(1) classes that declare pure virtual functions are called abstract classes;

(2) abstract classes can contain multiple pure virtual functions;

(3)The object of the abstract class cannot be declared, but the pointer variable and reference variable pointing to the abstract class can be declared.;

(4) abstract classes can also define other non-pure virtual functions;

(5) If the derived class does not redefine the pure virtual function in the base class, the virtual function must be declared as a pure virtual function in the derived class;

(6) An entity or abstract class can be derived from an abstract class, but an abstract class cannot be derived from a specific class (this experiment on vs2012 is incorrect, see the code below );

(7) In a complex class inheritance structure, the higher the class abstraction level, sometimes the implementation of some member functions cannot be provided. Obviously, abstract classes are special classes, it is generally in the outer layer of the class inheritance structure;

(8) The purpose of introducing abstract classes is to organize relevant classes in a class inheritance structure and provide unified operation interfaces for these classes through abstract classes.

Article 6 of verification:

class Base {public:    void f() { cout << "Base::f" << endl; }};class Base2 : public Base{public:    virtual void g()=0;};class Derive : public Base2{public:    void g() { cout << "Derive::g" << endl; }};int main() {    Base2 *b2 = new Derive;    b2->f();    b2->g();    return 0;}

Final output:
Base: f
Derive: g

The description can also be used to derive an abstract class from a specific class.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.