A brief analysis of C + + virtual function and virtual function table _c language

Source: Internet
Author: User
Tags class definition

The function of virtual function in C + + is mainly to realize the mechanism of polymorphism. About polymorphism, in short, is to use the pointer of the parent type to an instance of its subclass, and then call the member function of the actual subclass through the pointer to the parent class. This technique allows the pointer of the parent class to have "multiple forms," which is a generic technique. The so-called generic technology, plainly is to try to use immutable code to implement the variable algorithm. For example: template technology, RTTI technology, virtual function technology, or try to do at compile-time resolution, or try to do run-time resolution.
On the use of virtual functions, I do not do too much elaboration here. You can take a look at the relevant C + + books. In this article, I just want to from the virtual function of the implementation mechanism for everyone a clear analysis.
Of course, the same article on the internet also appeared some, but I always feel that these articles are not very easy to read, large sections of code, no pictures, no detailed instructions, no comparison, no extrapolate. Not conducive to learning and reading, so this is the reason I want to write this article. I also hope that members will give me more advice.
Let's go to the world of virtual functions together.

1. Definition of virtual function
A virtual function must be a non-static member function (and not a constructor) of a class whose access rights are public (which can be defined as private or proteceted, but for polymorphic, it is meaningless.) To define the general form of a virtual function in the class definition of a base class:
     Virtual function returns the name of a value type (formal parameter list)
{function Body}
the function of virtual function is to realize dynamic interlock, that is, in the running phase of the program, dynamically select the appropriate member function, after the virtual function is defined, you can redefine the virtual function in the derived class of the base class (the form is also: virtual function return value type virtual function name (formal parameter list) {function Body}), A function that is redefined in a derived class should have the same number of parameters and formal parameter types as the virtual function. To achieve a unified interface, different definition process. If a virtual function is not redefined in a derived class, it inherits the virtual function of its base class. When the program discovers the virtual function name, it will automatically be used as a dynamic binder, which is to select the appropriate member dynamically when the program is run.

It takes three conditions to implement a dynamic binder:
1, it is necessary to define the behavior of the dynamic binder as the virtual function of the public property of the class.
2. There is a subtype relationship between classes, typically manifested as a class derived from another class public.
3, you must first use the base class pointer to the object of the subtype, and then call the virtual function directly or indirectly using the base class pointer.

To define the limitations of a virtual function:
(1) Non-class member functions cannot be defined as virtual functions, and static member functions and constructors in class member functions cannot be defined as virtual functions, but destructors can be defined as virtual functions. In fact, a good programmer often defines a destructor of a base class as a virtual function. Because, after defining the destructor of a base class as a virtual function, the system invokes the corresponding destructor of the class when it deletes a pointer to the object defined by the derived class, using the Delete method. Instead of defining a destructor as a virtual function, only the destructor of the base class is called.
(2) You need to declare a function as a virtual function using the keyword "virtual" only in the class body in which the function is declared, but you do not need to use the keyword "virtual" when defining the function.
(3) If a member function is declared to be a virtual function, it cannot appear in the class that has the same name as the member function and the return value, number of arguments, and the same type of argument. In a derived class that is a base class of this class, you cannot also have the same parameter type function as the number of identical parameters with the return value of this non-virtual name.

Why a virtual function must be a member function of a class:
The purpose of virtual function is to realize polymorphism, it is useless to define virtual function outside of class.

Why a static member function of a class cannot be a virtual function:
if defined as a virtual function, then it is dynamically bound, which can be overridden in a derived class, with the definition of a static member function (: Only one copy in memory; Access to static members through a class name or an object reference) is inherently contradictory.

Why constructors cannot be virtual functions:
Because if the constructor is a virtual function, it will be constructed during execution, and the execution period requires the object to be established, and the constructor completes the work to establish the appropriate object, so it is not possible to perform polymorphism on the object that is not well constructed (the purpose of the virtual function is to achieve polymorphism). In the inheritance system, the order of the constructs is from the base class to the derived class, which is designed to ensure that the object is successfully built. The constructor also undertakes the establishment of the virtual function table, and if it is a virtual function, how to ensure the success of the VTBL construction?

  Note: What happens when a virtual function is inside the constructor of a base class ? The result is that the virtual function mechanism does not work in the constructor, calling the virtual function the same as calling a generic member function. How do you work when a virtual function is inside a destructor of a base class ? As with constructors, only the version of "local" is invoked. However, the same behavior, the reason is not the same. Constructors can only invoke "partial" versions because there is no information on the derived class version at the time of the call. The destructor is because the information of the derived class version is already unreliable. As we know, the invocation order of destructors, contrary to constructors, is a destructor from a derived class to a base class. When a destructor of a class is invoked, the destructor of its derived class has been invoked, and the corresponding data has been lost, and it is very dangerous to invoke the version of the derived class of the virtual function to manipulate some unreliable data. Therefore, in the destructor, the virtual function mechanism also does not function.

2. virtual function table
people who know C + + should know that virtual functions (virtual function) are implemented by a virtual function table (virtual table). Referred to as v-table. In this table, the master is the Address table of a virtual function of a class, which solves the problem of inheritance and coverage, and ensures that the actual function of the real response is true. In this way, an instance of a class with a virtual function (note: A class that has pure virtual functions cannot be instantiated.) The table in this instance is allocated in memory (note: A class's virtual function table is static, which means that for each instance of the class, his virtual function table is fixed and does not generate a corresponding virtual function table for each instance). So, when we manipulate a subclass with a pointer to a parent class, the virtual function table is important, and it's like a map that indicates the function that should actually be called.

Here we look at this virtual function table. In the C + + standard specification, the compiler must ensure that the pointer to the virtual function table exists at the very beginning of the object instance (this is to ensure that the offset of the virtual function is correctly fetched). This means that we get the virtual function table through the address of the object instance, and then we can iterate through the function pointer and call the corresponding function.

Let's say 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;}

};

According to the above, we can get the virtual function table of base based on the example of base. 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 ' the ' the ' the ' the ' the ' the ' the ' the ' The ' the ' (

int*) * (int*) (&b)

Pfun = (Fun) 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

With this example, we can see that we can get the address of the virtual function table by forcing the &b into int *, and then we can take the address of the first virtual function again, that is base::f (), which is validated in the above program (Put int* Force to a function pointer). With this example, we can see that if you want to invoke Base::g () and Base::h (), the code is as follows:

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

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

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

Draw a picture to explain. As shown below:

Note: In the above diagram, I add a node to the end of the virtual function table, which is the ending node of the virtual function table, just like the Terminator "/0" of the string, which flags the end of the virtual function table. The value of this ending flag is different under different compilers.

Under Winxp+vs2003, this value is null.

and under Ubuntu 7.10 + Linux 2.6.22 + GCC 4.1.3, this value is 1, which means that there is also the next virtual function table, and if the value is 0, the last virtual function table is represented.

Below, I will explain the appearance of the subclass virtual function table for "No overlay" and "overwrite" respectively. It is meaningless not to overwrite the virtual function of the parent class. The main purpose of my story about not covering is to give a comparison. In comparison, we can know more clearly the specific implementation within it.

(1), General inheritance (no virtual function overrides)
Next, let's look at what the virtual function table is like when inheriting. Suppose you have an inheritance relationship as shown below:

Note that in this inheritance relationship, subclasses do not have functions that override any of the parent classes. Then, the virtual function table for an instance of a derived class looks like this:

For example: derive D; The virtual function table is as follows: (Overload (Overload) and override (Overrides), overload is called the same name and the signature is different, the rewrite is a subclass of the virtual function of the realization. )

We can see the following points:

1 virtual functions are placed in the table in the order in which they are declared.

2 The virtual function of the parent class is in front of the virtual function of the subclass.

(2), General inheritance (with virtual function overrides)
It is obvious that the virtual function of the parent class is overridden, otherwise the virtual function becomes meaningless. Now, let's take a look at what it would look like if a virtual function in a subclass overloaded the virtual function of the parent class. Suppose we have one of these inheritance relationships.

In order for you to see the effect of being inherited, in the design of this class, I covered only one function of the parent class: F (). Then, the virtual function table for an instance of a derived class would look like the following:

We can see the following points from the table,

1 the covering F () function is placed in the position of the original parent virtual function in the Subclass virtual function table.

2 functions that are not covered are still.

In this way, we can see that for programs like the following,

Base *b = new Derive ();

B->f ();

The position of F () of the virtual function table (the subclass's virtual function table), which is referred to by B, has been replaced by the Derive::f () function address, so that the Derive::f () is invoked when the actual call occurs. This enables polymorphism.

(3), multiple inheritance (no virtual function overrides)
Next, let's look at the situation in multiple inheritance, assuming that there is an inheritance relationship for the following class. Note: Subclasses do not have functions that overwrite the parent class.

For a virtual function table in a subclass instance, the following looks like this:

We can see:

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 so-called first parent class is judged in the Order of Declaration)

This is done in order to solve the different parent class type of pointer to the same subclass instance, and can call to the actual function.

(4), multiple inheritance (with virtual function overlay)
Let's take a look at the case where a virtual function overlay occurs.

In the following illustration, we overwrite the F () function of the parent class in the subclass.

The following is a diagram of a virtual function table in a subclass instance:

We can see that the position of F () in the three parent class virtual function table is replaced by the function pointer of the subclass. In this way, we can use any parent class pointer to point to the subclass and invoke the subclass's F (). Such as:

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. Safety
Every time you write a C + + article, you always have to criticize C + +. This article is no exception. Through the above, I believe we have a more detailed understanding of the virtual function table. Water may carry a boat, and it can also overturn. Now, let's see if we can do something bad with a virtual function table.

Try 1: To access the subclass's own virtual function through a pointer to a parent type (pointing to a subclass object)
We know that it is meaningless for subclasses not to overload the virtual functions of the parent class. Because polymorphism is also based on a function overload. Although in the above diagram we can see that the subclass's virtual table has derive its own virtual function, we simply cannot use the pointer of the base class to invoke the subclass's own virtual function:

Base1 *b1 = new Derive ();

B1->F1 (); Compilation error

Any attempt to use a parent class pointer to invoke a member function in a subclass that does not overwrite the parent class is considered illegal by the compiler, so that such a program cannot be compiled at all.

But at run time, we can access the virtual function table through the way of pointer to violate C + + semantics behavior.

Attempt 2: To access the non-public virtual function of the parent class through a pointer to a parent type (pointing to a subclass object)
In addition, if the virtual functions of the parent class are private or protected, but these are not public virtual functions will also exist in the subclass virtual function table, so we can also access the virtual function table to access these non-public virtual functions, this is very easy to do.

Such as:

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

}

4. Summary

C + + This language is a magic language, for programmers, we never seem to understand what the language behind us. To be familiar with the language, we need to understand the things in C + + and need to understand the dangerous things in C + +. Otherwise, it is a programming language that moves a stone to its own feet.

Appendix I: VC View virtual function table

We can see the virtual function table (not very complete) by expanding instances of the class in the debug state of the VC IDE environment.

Appendix II: Routines
The following is a routine of virtual function table access for multiple inheritance:

 
#include <iostream> 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" <& Lt 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<<pfun<<endl;
      Base2 ' s vtable//pfun = (Fun) * ((int*) * (int*) ((int*) &d+1) +0);
      Pfun = (Fun) pvtab[1][0];
 
      Pfun ();
      Pfun = (Fun) * (int*) * (int*) ((int*) &d+1) +1);
      Pfun = (Fun) pvtab[1][1];
 
      Pfun ();
      Pfun = (Fun) pvtab[1][2];
 
      Pfun ();
      The tail of the vtable Pfun = (Fun) pvtab[1][3]; Cout<<pfun<<eNdl
      Base3 ' s vtable//pfun = (Fun) * ((int*) * (int*) ((int*) &d+1) +0);
      Pfun = (Fun) pvtab[2][0];
 
      Pfun ();
      Pfun = (Fun) * (int*) * (int*) ((int*) &d+1) +1);
      Pfun = (Fun) pvtab[2][1];
 
      Pfun ();
      Pfun = (Fun) pvtab[2][2];
 
      Pfun ();
      The tail of the vtable Pfun = (Fun) pvtab[2][3];
 
      cout<<pfun<<endl;
return 0; }

  above is about C + + virtual function and virtual function table all the analysis, I hope to help you learn.

Related Article

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.