function semantics
C + + supports three types of memberfunctions:static, nonstatic, and virtual, each of which is called differently.
The static function has two characteristics: it cannot access the nonstatic data directly; it cannot be declared as Const.
A member of various calling methods
1. Nonstaticmember Functions
One of the design guidelines for C + + is that nonstaticmember function must be at least as efficient as the general nonmemberfunction.
If normalize () is a virtualmember function, then call:
Ptr->normalize ();
The conversion steps for the compiler are:
① rewrite function signature (function prototype) to add an additional parameter to MemberFunction
, the function is called by Classobject to provide an access pipeline.
② will change the access operation for each "Nonstaticdata member" to be stored via the this pointer
Take.
③ writes memberfunction to an external function. "Mangling" The name of a function
Processing, making it a unique vocabulary in the program.
2. Virtualmember Functions
If normalize () is a virtualmember function, then call:
Ptr->normalize () will be converted to (*ptr->vptr[1]) (PTR);
which
N Vptr indicates that a compiler-generated pointer is pointing to virtual table. It is placed in each of the classobject that "declares (or inherits from) one or more Virtualfunctions". In fact its name will also be "mangled", because in a complex class derivation system, there may be multiple vptrs.
N 1 is the index value of the virtualtable slot, associated to the normalize () function.
n the second PTR represents the this pointer.
And for calls:
Obj.normalize ();
The compiler does not need to convert it to:
(*obj.vptr[1]) (&obj);
But it will be treated as a general nonstaticmember function. Resolution:
NORMALIZE_7POINT3DFV (&obj);
3. Staticmember Function
If Point3d::normalize () is a staticmember function, two call actions:
Obj.normalize ();
Ptr->normalize ();
will be converted to a general nonmember function call, like this:
Obj.normalize ();
NORMALIZE_7POINT3DSFV ();
Ptr->normalize ();
NORMALIZE_7POINT3DSFV ();
For the following wording:
((point3d*) 0)->object_count ();
where Object_count () is simply passed back to _object_count this staticdata member.
Before introducing Staticmember functions, all memberfunctions required in the C + + language must be called through the object of the class. In fact, a class object is required only if one or more Nonstaticdata members are accessed directly in the memberfunction. Classobject provides the this pointer for use in this form of a function call. If no members are accessed directly, the this pointer is not required, so it is not necessary to invoke a member function through a classobject.
The main feature of Staticmember functions is that it does not have this pointer. The secondary features are all rooted in their attributes:
n It is not able to directly access the nonstaticmembers in its class.
n It can not be declared as const, volatile, or virtual (there is nonstaticmembers in the implied function).
N He does not need to be called through classobject-although it is called this way most of the time.
Similar to Staticmember data, if you take a staticmember function address, you get its in-memory address. Such as:
&point3d::object_count ();
A value is given, and the type is:
Unsignedint (*) ();
Instead of:
Unsignedint (Point3D::*) ();
The Static member function is almost equivalent to nonmemberfunction because it lacks this pointer. It provides an unexpected benefit: to become a callback function.
Two virtual Member Functions
We've seen the general implementation model of Virtualfunction: Each class has a virtualtable, contains the address of the function virtualfunction in the class, and each object has a vptr, Point to the location of the virtualtable.
1. Virtualfunction under single-weight inheritance
A class will only have one virtualtable (single inheritance). The address of each of the corresponding class object in the Activevirtual function entity. These activevirtual function include:
n the function entity defined by this class. It overrides (overriding) an BaseClass virtual function entity that might exist.
N inherits the function entity from BaseClass, which appears only when derived class decides not to rewrite Virtualfunction.
n a pure_virtual_called () function entity that can act as a space-defending role for the purevirtual function or as an exception-handling function at execution time.
Each virtualfunction is assigned a fixed-point index value that remains associated with a particular virtualfunction throughout the inheritance system. For example, in our point class system:
Classpoint
{
Public
Virtual ~point ();
Virtual point& mult (float) = 0;
//...... Other operations
float X () const {return _x;}
Virtual float y () const {return 0;}
Virtual float Z () const {return 0;}
Protected
Point (float x = 0.0);
float _x;
};
The memory layout is as follows:
When a class is derived from point, such as CLASSPOINT2D:
Class Point2d:public Point
{
Public
POINT2D (floatx = 0.0, float y = 0.0): Point (X), _y (y) {}
~POINT2D ();
Rewrite BaseClass virtual functions
Point2d&mult (float);
Floaty () const {return _y;}
//......
Protected
float_y;
};
A total of three possibilities:
1) It can inherit the function entity of virtual functions declared by BaseClass. correctly, the address of the function entity is copied to the DerivedClass's virtual table-relative slot.
2) It can use its own function entity (function weight write). This means that its own function entity address must be placed in the corresponding slot.
3) It can be added to the new virtualfunction. At this point, the size of virtual table will be increased by one slot, and the new function entity address will be placed in the slot.
POINT2D's virtualtable points to destructor in slot1 and Slot2 () in mult (replacing Purevirtual function). its own Y () function entity is placed in the SLOT3. The z () function entity address that inherits from point is placed in the SLOT4.
In a similar case, Point3D derives from point2d, as follows
Classpoint3d:public point2d
{
Public
POINT2D (Float x = 0.0, float y = 0.0,float z = 0.0)
: point2d (x, y), _z (z) {}
~point3d ();
Rewrite BaseClass virtual functions
point3d& mult (float);
float Z () const {return _z;}
//......
Protected
float _z;
};
The slot1 in its virtualtable prevents Point3D Destructor,slot from placing the Point3d::mult () function address. SLOT3 places the address of the Y () function that inherits from POINT2D, Slot4 places its own Z () function address.
Now for the equation:
Ptr->z ();
So, do we have enough to just set Virtualfunction's call at compile time?
n Generally, we do not know the true type of the object that PTR refers to. I know, however, that the virtualtable of the object can be accessed via PTR.
n Although I don't know that the Z () function entity is called, I know that every Z () function address is placed in the SLOT4
The only thing that can be known at the time of execution is which Z () function entity the SLOT4 refers to.
2. Virtualfunction under Multiple inheritance
Support for virtualfunction in multiple inheritance systems, whose complexity revolves around the second and subsequent base class, and the "must adjust this pointer during execution", for example, the class system:
ClassBase1
{
Public
Base1 ();
Virtual ~base1 ();
virtual void speackclearly ();
Virtual BASE1 *clone () const;
Protected
float data_base1;
};
ClassBase2
{
Public
Base2 ();
Virtual ~base2 ();
virtual void mumble ();
Virtual base2* clone () const;
Protected
float Data_base2;
};
Classderived:public Base1, Public Base2
{
Public
Derived ();
Virtual ~derived ();
Virtual derived* clone () const;
Protected
float data_derived;
};
"Derived support virtual function" of the difficulty, all fall on the base2subobject body, there are three problems to be solved, in this case is (1) virtualdestructor, (2) inherited Base2:: Mumble (), (3) A set of Clone () function entities.
First, I assign the address of a derived object that is configured from the heap to a Base2 pointer
base2* pbase2 = newderived;
The address of the new derived object must be adjusted to point to its base2subobject. The following code will be generated at compile time:
Transfer to a second BaseClass
derived* temp = newderived;
base2* Pbase2 = temp? Temp + sizeof (BASE1): 0;
Without such an adjustment, any "non-polymorphic use" of the pointer (to the following) would fail:
Even if PBASE2 is assigned a derived object, that's fine.
pbase2->data_base2;
When the programmer wants to delete the object that Pbase2 refers to:
The correct Virtualdestructor function entity must be called first
Then execute the delete operator
Pbase2 may need to be adjusted to indicate the starting point of the complete object
Delete Pbase2;
The pointer must again be adjusted to point again at the beginning of the derived object (presumably it also points to the derived object). However, the above offset addition cannot be set directly at compile time, because the real object referred to by PBASE2 can only be determined by the execution period.
The "Necessary this pointer adjustment" operation brought by the delete operation must be completed during the execution period.
Under multiple inheritance, a derivedclass contains n-1 additional virtualtable. n represents the number of baseclass on its previous layer (so there is no additional virtual table for single inheritance). For this regular session there are two virtualtable generated by the compiler:
(1) A primary entity, shared with BASE1 (leftmost baseclass)
(2) A secondary entity related to BASE2 (second BaseClass)
The layout of virtualtable under multiple inheritance is as follows.
The three scenarios described in the figure are as follows:
(1) Call derived class virtual with a pointer to the second BaseClass
function such as:
base2* ptr = new Derived;
Delete ptr;
From there, you can see the focus of the invoke operation: PTR points to the base2subobject of the derived object, and for proper execution, PTR must adjust the start address to the derived object.
(2) A pointer to "DerivedClass" is invoked to invoke the second base class in a
inherited from the virtualfunction. In this case, the derived class pointer must be adjusted again to point to the second basesubobject. For example:
derived* pder = new Derived;
Call Base2::mumble ();
Pder must be adjusted forward sizeof (BASE1) a bytes
Pder->mumble ();
(3) The third situation occurs under the extended nature of a language. Detail slightly.
Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.
C + + object model function layout