Document directory
- Virtual function table
- General inheritance (no virtual function overwrite)
- General inheritance (with virtual function overwrite)
- Multi-inheritance (no virtual function overwrite)
- Multiple inheritance (with virtual function overwrite)
- Security
The definition of virtual functions follows the following important rules:
1. if a virtual function appears in a base class and a derived class, it only has the same name, and the form parameter is different, or the return type is different, even if the virtual keyword is added, it will not be delayed.
2. Only member functions of a class can be described as virtual functions. Because Virtual functions are only suitable for class objects with inheritance relationships, common functions cannot be described as virtual functions.
3. static member functions cannot be virtual functions, because static member functions are not restricted to an object.
4. the inline function cannot be a virtual function, because the inline function cannot dynamically determine the position during running. Even if a virtual function is defined within a class, the system still regards it as non-inline during compilation.
5. the constructor cannot be a virtual function, because during the construction, the object is still a space with a fixed position. Only after the construction is complete, the object is an instance of a specific class.
6. destructor can be virtual functions and are generally known as virtual functions.
Although we say that using virtual functions reduces efficiency, it is always advantageous to define all member functions in a class as virtual functions as the processing speed is getting faster and faster, in addition to adding some additional overhead, it has no other disadvantages and is good for ensuring the encapsulation characteristics of classes.
For the important rule 6 used by the preceding virtual function, it is necessary to describe it with an instance,It is necessary to declare the destructor of classes with polymorphism as virtual.
The Code is as follows:
# INCLude <IOSTrEAM>
Using namespace STD;
Class Vehicle
{
Public:
Vehicle (float speed, int total)
{
Vehicle: speed = speed;
Vehicle: Total = total;
}
Virtual VoIDShowmember ()
{
Cout <}
Virtual ~ Vehicle ()
{
Cout <"loading vehicle-based class destructor" <
Cin. Get ();
}
Protected:
Float speed;
Int total;
};
Class car: Public Vehicle
{
Public:
Car (int aird, float speed, int total): Vehicle (speed, total)
{
Car: AIRD = AIRD;
}
Virtual void showmember ()
{
Cout <}
Virtual ~ Car ()
{
Cout <"loading car derived class destructor" <
Cin. Get ();
}
Protected:
Int AIRD;
};
Void test (Vehicle & temp)
{
Temp. showmember ();
}
Void delpn (Vehicle * temp)
{
Delete temp;
}
Void main ()
{
Car * A = new car (100,1, 1 );
A-> showmember ();
Delpn ();
Cin. Get ();
}
From the running results of the preceding Code, when delpn (a); is called, the system successfully determines that the car class destructor is called first, if you remove the virtual modifier of The Destructor and observe the result, you will find that the destructor of the base class is always called, the virtual modifier of polymorphism is not only necessary for common member functions of the base class and the derived class, but also important for the destructor of the base class and the derived class.
Detailed description of virtual tables
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 make resolutions 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.
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, 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. 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.
After hearing that, I can feel that you are more dizzy than before. It doesn't matter. The following is an actual example. I believe you will understand it at a glance.
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 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 ()
You should understand it at this time. What? Still a little dizzy. Yes, the code looks too messy. No problem. Let me draw a picture to explain it. 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 end flag has different values 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.
Below, I will explain the virtual function tables 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 overload any function of the parent class. In the instance of the derived class, the virtual function table is as follows:
The following table lists the virtual functions of instance: derive D:
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.
I believe that you can write a program for verification by referring to the previous program.
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 table.
2) The unoverwritten functions remain.
In this way, we can see the following program,
Base * B = new derive ();
B-> F ();
The F () Position of the virtual function table in memory referred to by B has been replaced by the derive: F () function address, so when the actual call occurs, it is 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 direct any static parent class 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 ()
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.
1. Use a pointer of the parent type 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 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 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. (I believe you can do this by reading the code in the appendix below)
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 *) (& D) + 0 );
Pfun ();
}
(From http://wanderer-zjhit.blogbus.com/logs/161830653.html)