C ++ virtual functions and virtual function table Parsing

Source: Internet
Author: User

C ++ virtual functions and virtual function table Parsing
1. Definition of virtual functions

A virtual function must be a non-static member function (and a non-constructor) of the class. Its access permission can be defined as private or proteceted, but it is meaningless for polymorphism .), Define the general form of virtual functions in the class definition of the base class:
Virtual function return value type virtual function name (parameter table)
{Function body}
The role of a virtual function is to implement dynamic association, that is, to dynamically select an appropriate member function in the running stage of the program. After defining a virtual function, you can redefine the virtual function in the derived class of the base class (Format: virtual function return value type virtual function name (parameter table) {function body }), the new function in the derived class should have the same number of parameters and type as the virtual function. To implement unified interfaces and different definition processes. If the virtual function is not redefined in the derived class, it inherits the virtual function of its base class. When the program finds the keyword virtual before the virtual function name, it automatically uses it as the dynamic concatenation processing, that is, it dynamically selects the appropriate Member when the program is running.

Three conditions are required for dynamic Association:

1. the behavior that requires dynamic association must be defined as a virtual function of the Public attribute of the class.
2. There is a Subtype Relationship between classes. Generally, a class is derived from another class.
3. You must first use a base class pointer to point to a child type object, and then directly or indirectly use a base class pointer to call a virtual function.

Limits on defining virtual functions:

(1) Non-class member functions cannot be defined as virtual functions, and static member functions and constructors cannot be defined as virtual functions in class member functions, however, you can define the Destructor as a virtual function. In fact, good programmers often define basic class destructor as virtual functions. Because after you define the destructor of the base class as a virtual function, the system calls the corresponding class destructor when you delete an object pointer to the derived class by using delete. Instead of defining a destructor as a virtual function, only the basic class's destructor are called.
(2) You only need to use the keyword "virtual" in the class body of the declared function to declare the function as a virtual function, but do not need to use the keyword "virtual" when defining the function ".
(3) If a member function is declared as a virtual function, a non-virtual function with the same name and return value, number of parameters, and parameter type cannot appear in this class. In a derived class of the base class, such non-virtual functions with the same name and return values as the number of parameters and parameter type functions cannot appear.

 

Why must a virtual function be a member function of the class:

The purpose of the creation of virtual functions is to achieve polymorphism, and defining virtual functions outside the class is useless.

 

Why cannot static member functions of a class be virtual functions:

If it is defined as a virtual function, it is dynamically bound, that is, it can be overwritten in the derived class, which is defined with the static member function (: only one copy in the memory; accessing static members through class names or object references.

 

Why cannot the constructor be a virtual function:

Because if the constructor is a virtual function, it will be constructed during the execution period, and the object needs to be created during the execution period. The work done by the constructor is to create an appropriate object, therefore, it is impossible to execute polymorphism (the purpose of a virtual function is to implement polymorphism) on an object that has not been constructed. In the inheritance system, the construction sequence is from the base class to the derived class. The purpose is to ensure that the object can be successfully constructed. The constructor is responsible for creating the virtual function table. If it is a virtual function, how can we ensure the vtbl construction is successful?

Note: When the base class constructor has a virtual functionWhat will happen? The result is that in the constructor, the virtual function does not work. Calling a virtual function is the same as calling a member function.When there is a virtual function in the destructor of the base classAnd how to work? Similar to the constructor, only the "local" version is called. However, the behavior is the same for different reasons. The constructor can only call the "local" version because the information of the derived class version is not available at the time of the call. The Destructor is because the information of the derived class version is no longer reliable. We know that the calling sequence of the Destructor is the opposite to that of the constructor, from the destructor of the derived class to the destructor of the base class. When the destructor of a class is called, The destructor of its derived class has been called, and the corresponding data has been lost. If you call the version of the derived class of the virtual function, it is very dangerous to operate on some unreliable data. Therefore, the virtual function mechanism does not work in destructor.

 

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 (Or interface) 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.

I will not elaborate on the usage of virtual functions too much here. You can read related C ++ books. In this article, I just want to give you a clear analysis of the implementation mechanism of virtual functions.

Of course, the same article has also appeared on the Internet, but I always feel that these articles are not very easy to read. There are no pictures, no detailed descriptions, and no comparison, no. It is not conducive to learning and reading, so this is why I want to write this article. I hope you will give me more comments.

Let's get down to the truth and let us enter the world of virtual functions together.

2. virtual function table

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, instances of classes with virtual functions (Note: classes with pure virtual functions in abstract classes cannot be instantiated .) The table is allocated to the memory of this instance (note: the virtual function table of a class is static, that is, for each instance of this class, its virtual function table is fixed and does not generate a corresponding virtual function table for each instance .), Therefore, when we use the pointer of the parent class to operate a subclass, this virtual function table becomes very important, just like a map, specifies the function to be called.

Here we will focus on this virtual function table. In the standard specification of C ++, the compiler must ensure that the pointer to the virtual function table exists in the front of the object instance (this is to ensure that the offset of the virtual function is obtained correctly ). 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.

 

Suppose we have a class like this:

Class Base {

Public:

Virtual void f () {cout <"Base: f" <endl ;}

Virtual void g () {cout <"Base: g" <endl ;}

Virtual void h () {cout <"Base: h" <endl ;}

};

As mentioned above, we can use the Base instance to obtain the Base virtual function table. The following is the actual routine:

{

...

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 the first virtual function

PFun = (Fun) * (int *) (& B ));

PFun ();

...

}

The actual running results are as follows (Windows XP + VS2003, Linux 2.6.22 + GCC 4.1.3 ):

Virtual function table address: 0012FED4

Virtual function table-first function address: 0044F148

Base: f

 

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 ). Through this example, we can know that if you want to call Base: g () and Base: h (), the Code is as follows:

(Fun) * (int *) (& B) + 0); // Base: f ()

(Fun) * (int *) (& B) + 1); // Base: g ()

(Fun) * (int *) (& B) + 2); // Base: h ()

 

Draw a picture to explain. As follows:

 

Note: in the above figure, I add a node to 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.

 

The following describes the virtual function tables of sub-classes for "No overwrite" and "Overwrite" respectively. It is meaningless to not override the virtual functions of the parent class. The reason why I want to talk about the situation without coverage is mainly to give a comparison. In comparison, we can better understand the specific internal implementation.

General inheritance (no virtual function overwrite)

Next, let's take a look at what the virtual function table looks like during inheritance. Assume there is an inheritance relationship as follows:

 

Note that in this inheritance relationship, the subclass does not override any function of the parent class. The following table lists the virtual functions of the instance in the derived class:

For the table of virtual functions of instance: Derive d; (overload) and override (rewrite), the reload is called the same name and the signature is different, rewrite is to re-implement the virtual function of the subclass .)

 

We can see the following points:

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.

 

General inheritance (with virtual function overwrite)

Overwriting the virtual functions of the parent class is obvious. Otherwise, the virtual functions become meaningless. Next, let's take a look at what it will look like if there is a virtual function in the subclass that reloads the virtual function of the parent class? Suppose we have the following inheritance relationship.

 

In order to let everyone see the inherited effect, in the design of this class, I only covered a function of the parent class: f (). Then, the virtual function table of the instance of the derived class will look like the following:

 

We can see the following points from the table,

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

2) The unoverwritten functions remain.

In this way, we can see the following program,

Base * B = new Derive ();

B-> f ();

The position of f () in the memory virtual function table (virtual function table of subclass) referred to by B has been replaced by the Derive: f () function address, therefore, when the actual call occurs, Derive: f () is called. This achieves polymorphism.

Multi-inheritance (no virtual function overwrite)

Next, let's take a look at the multi-inheritance situation. Suppose there is an inheritance relationship of the following class. Note: The subclass does not overwrite the function of the parent class.

 

The following figure shows the virtual function table in the subclass instance:

 

We can see that:

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)

In this way, the actual function can be called to resolve the pointer of different parent classes pointing to the same subclass instance.

Multiple inheritance (with virtual function overwrite)

Next let's take a look at the case of virtual function coverage.

, We override the f () function of the parent class in the subclass.

 

The following figure shows the virtual function table in the subclass instance:

 

We can see that the f () position in the three parent class virtual function tables is replaced with the function pointer of the subclass. In this way, we can use any parent class pointer to point to the subclass and call the f () of the subclass. For example:

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 ()

 

 

3. Security

Every time I write a C ++ article, I always have to criticize C ++. This article is no exception. As described above, I believe we have a more detailed understanding of the virtual function table. The water can carry boat, but also the boat. Next, let's take a look at what we can do with a virtual function table.

 

Try: Use a pointer of the parent type (pointing to a subclass object) to access the virtual function of the subclass.

We know that it is meaningless for the subclass to overload the virtual function of the parent class. Because polymorphism is also based on function overloading. Although we can see in the above figure that the virtual table of the subclass has the Derive virtual function, it is impossible to use the pointer of the base class to call its own virtual function of the subclass:

Base1 * b1 = new Derive ();

B1-> f1 (); // compilation Error

Any attempt to use the parent class pointer to callThe member functions of the parent class are not overwritten.Such a program cannot be compiled.

However, during runtime, we can access the virtual function table through pointers to violate the C ++ semantics.

 

Attempt: access non-public of the parent class through the parent type pointer (pointing to the subclass object) 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 subclass virtual function table, therefore, 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 *) (& d) + 0 );

PFun ();

}

4. Conclusion

C ++ is a Magic language. For programmers, we can never find out what we are doing with this language. To be familiar with this language, we must understand the things in C ++ and the dangerous things in C ++. Otherwise, this is a programming language for throwing stones at your own feet.

 

Note: original article link

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.