Objective
C + + member functions are divided into static functions, non-static functions and virtual functions three kinds, in this series of articles, many mentions static and non-static does not affect the memory occupied by objects, and virtual functions need to introduce virtual pointers, so you need to adjust the memory layout of the object. Now that you have solved the problem of the layout of the data, functions, etc. in memory, the next thing to consider is how to invoke, the three functions mentioned above are different calling mechanisms, and the difference between the two is exactly what this blog needs to discuss.
Non-static member functions
One of the design guidelines for C + + is that non-static member functions must be at least as efficient as the general non-member functions. To achieve this, member properties of member functions do not add to the burden. Consider the following two function calls:
int Getage (Animal *_this);//Non member function
int animal::getage ();//member function
The Getnum function is defined as follows:
int Getage () {
return age;
}
The former needs to pass in a class pointer, which belongs to a member function call, which directly indicates the function call of the animal class. Essentially, the two functions are the same, because the compiler converts the latter to the former, and the conversion steps are as follows:
1. Rewrite the function's prototype so that it accepts an extra parameter, which is the function's this pointer:
int Animal::getnum (Animal *this);//Insert a This pointer within the function
2. Change the access of each non-static member variable to be accessed through the this pointer:
{
return this.age;
}
3. Rewrite the member function to an external function whose name is processed by "mangling" to make it known as a unique vocabulary in the program, such as the above function may be handled as: GETNUM_ANIMALFV (P), where the name is guaranteed to not conflict!
In this way, the extern "C" Operation suppresses the "mangling" effect of the function name and is used to invoke the C function in C + +.
So, the key to rewriting a member function to a non member function is two: one is the channel that can provide the function with a read-write member variable, and the other is to resolve the name conflict that may be brought. The former is well resolved by passing a this pointer, which ensures the uniqueness of the name by means of a certain name-conversion rule.
Virtual member functions
Let's think about it. If a virtual function exists in a class, the compiler does the following three things:
1. Assign a virtual function table to the class that holds the virtual function at the executor's address
2. Insert a virtual pointer in the class to point to the virtual table of the class
3. Store the entry address of each virtual function in the virtual function table corresponding slot
So if you want to call a virtual function correctly, you just need to find the virtual function in the corresponding position in the virtual function table, so consider the following example.
Class animal{
Public
Char name[10];//animal Name
int weight;//Weight
virtual void Eat () {}
};
Animal *animal;
Animal->eat ();
When a virtual function eat is invoked, the compiler is automatically converted to the following code:
Vptr is a pointer to a virtual function table, eat stored in the first position of the virtual function,
Because it is a member function, the function must also pass in a this pointer parameter
(* animal->vptr[0]) (animal);
Only if the pointer and the reference can show polymorphic forms, what if we display the call or invoke it directly with the class object?
Show Call eat function
Animal::eat ();
Directly using the object to invoke the
Animal Animal;
Animal.eat ();
In both of these calls, the former suppresses the virtual mechanism and invokes the Eat () directly as a non-static member function. For the latter, suppose the compiler converts it to the following form:
(* animal.vptr[0]) (&animal);
This is semantically correct, but there is absolutely no need to do so, so the compiler treats it directly as a animal::eat () display call.
Virtual function calls under single inheritance
When a class inherits from a base class, the virtual functions in it can occur in the following three cases:
1. Virtual functions in subclasses override virtual functions of the parent class
2. A virtual function entity that inherits from the base class, which is present in the base class, is not overridden in a subclass
3. A pure virtual function, used to "Occupy" in the virtual function table, or as an exception handler function of the actuator
For example, in three cases, subclasses do the following when building their own virtual function tables:
1. When the virtual function of the parent class is rewritten, the corresponding slot in the virtual function table is rewritten as the virtual function entry address of the subclass.
2. When inheriting a virtual function instance of a base class, simply copy the instance's address to the virtual function table of the subclass
3. Subclasses can define their own instances of virtual functions, archived in the slot of virtual tables, and the dimensions of virtual tables will increase
Here's a reference to an example that posting previously talked about, taking into account the following inheritance relationships:
Its memory layout is as follows:
You can see that the virtual function table is rewritten in subclasses, so how do you call the virtual function for such inheritance? We can observe that the relative position of the function in the virtual function table of the parent class is unchanged in the subclass, for the following call:
void Fun (Dog *dog) {
Dog->eat ();
}
dog* Dog = new Dog ();
animal* Animal = new Dog ();
Fun (dog);//The first way to invoke a direct pass to a dog pointer
Fun (animal);//The second invocation method, passing in a animal pointer
If you are passing in an object pointer to a dog class, so directly using the method in the previous section, if you pass in an object pointer to a animal class, we can see that, as always, we can take the method in the previous section because the position of eat () in the virtual function table has not changed, The only thing that is known at the execution time is: which eat () function is invoked.
virtual function invocation under multiple inheritance
With the above understanding, we know that a virtual function invocation is nothing more than two points to meet:
1. Need to know the address of the virtual function table
2. Need to know the position of the virtual function in the virtual function table
However, in multiple inheritance, this becomes somewhat more complex, with multiple virtual tables in multiple inheritance, such as the following inheritance relationships and memory layouts:
Its memory layout is as follows:
Or take the fun function above as an example, consider the following invocation methods:
Dog *dog = new Dog ();
Dog->eat ();//The first invocation method, passing directly into a dog pointer
Dog->sleep ();//The second invocation method, passing in a animal pointer
Dog->jump ();//The third invocation way, passing in a Canidae pointer
For the first two invocation methods, the invocation is basically similar to the previous section and does not need to change the this pointer because the beginning of the first bitwise inheritance class is the same as the beginning of the subclass object. For the third invocation, it seems a bit complicated. If you continue to pass in a not-adjusted this pointer, it is difficult to get Canidae's virtual table address. Here we first introduce a thunk method. The role of thunk is:
1. Adjust the this pointer with appropriate offset
2. Jump to the corresponding virtual function in accordance with the idea of thunk, and then call the jump () function, the this pointer needs to do the following adjustments:
Thunk
This+=sizeof (Animal);
Dog::eat (this);
Well, our problem becomes a multiple inheritance relationship, except that the first bit of the inheritance order, other bits of the class to implement virtual function calls need to make some adjustments. This adjustment occurs in the following two situations:
One, point a base class pointer to a subclass, of course, is the inheritance order after the first bit of the base class
Canidae *canidae = new Dog ();
Second, use the subclass pointer to call the base class function, of course, is the inheritance order after the first bit of the base class function
Dog Dog = new Dog ();
Dog->jump ();
In the first case, you need to adjust the Canidae pointer backward to the corresponding base class portion of the subclass sizeof (Animal) bit.
In the second case, you need to adjust the dog pointer backward sizeof (ANIMAL) bit and point to the Canidae base class section in dog.
As a result, virtual function calls under multiple inheritance are easier to understand, do you understand?
virtual function invocation under dummy inheritance
For virtual inheritance, the address of the virtual base class is stored in the memory layout for different compilers, and the book is directly like a maze. Well, I came to explore the origin of the purpose, by the author of this statement is really scared.
In virtual function calls under dummy inheritance, the complex point is still how to adjust the this pointer, and virtual inheritance has a virtual base class pointer over multiple inheritance, which makes the situation complex and changeable.
The author finally gives a definition: Do not define non-static member variables in the virtual base class, and want to be afraid these will affect the placement of the virtual base class pointer in memory, thus increasing the complexity of determining the appropriate offset.
Static member functions
The biggest difference between a static member function and the other member function is that it has no this pointer, and its main characteristics are:
1. It can not directly access the Non-static member variables in its class
2. It cannot be declared as const, volatile or virtual
3. It does not need to be invoked through class objects
Therefore, calls to static member functions are almost equivalent to non-member function calls. Of course, in order to indicate that he is a class member function, it is necessary to add class information to the naming adjustment as follows:
Nimal::getage ()//Suppose Getage is a static member function
It is named after the adjustment as follows:
GETAGE_ANIMALSFV ();//SFV indicates that he is a static member function that has a blank argument list (void)
Summarize
This blog explains the three class member function (non-static, static, virtual function) of the underlying call mechanism, and C + + for function naming, this pointer adjustment rules. We can see that C + + is on the member function call, for static, non-static member functions are basically equivalent to non-members function in the function invocation efficiency, but the virtual function calls to satisfy the polymorphism, need to adjust this pointer, find the virtual table address and so on operation, affect its function call efficiency, but these are also worth!