Fourth Chapter function semantics (the semantics of function)If there is a Point3D pointer and object:
Point3D obj; Point3D *ptr = &obj;
When doing this:
Obj.normalize ();p tr->normalize ();
, what happens? The Point3d::normalize () is defined as follows:
Point3D point3d::normalize () const {Register FLOAT mag = magnitude (); Point3D normal;normal._x = _x/mag;normal._y = _y/mag;normal._z = _z/mag;return normal;}
The Point3d::magnitude () is defined as follows:
float Point3d::magnitude () const {return sqrt (_x * _x + _y * _y + _z * _z);}
The answer is: I don't know.
C + + supports three types of member functions:static,nonstatic,virtual, each of which is called differently. The difference is the subject of the next section. However, although it is not possible to determine the normalize () and magnitude () two functions are virtual or nonvirtual, but can be determined that it must not be static for two reasons: (1) It accesses nonstatic data directly; (2) It is declared as Const,static member Functions can't do this two points.
4.1 Various invocation methods of memberRecalling history, the original "C with Classes" only supports nonstatic member functions. The Virtual function was added in the mid 1980s and is subject to many doubts. The Static member functions is the last type of function introduced, which was formally added to C + + in 1987.
nonstatic Member Functions (non-static member function)One of the design guidelines for C + + is that nonstatic member functions must be at least as efficient as the general nonmember function. That is, if you want to choose between the following two functions:
Float Magnitude3d (const Point3D *_this) {...} float Point3d::magnitude3d () const {...}
The choice of member function should not impose an additional burden because the "member function entity" has been converted internally by the compiler to a peer nonmember function entity.
For example, here is a nonmember definition for magnitude ():
Float Magnitude3d (const Point3D *_this) {return sqrt (_this->_x * _this->_x + _this->_y * _this->_y + _this-> ; _z * _this->_z);}
At first glance, it seems that the nonmember function is less efficient, indirectly through the parameters of the coordinate member, and the member function does directly take the coordinate member, but in fact member function is internalized into the form of nonmember, Here is the conversion step:
1. Rewrite the function's signature (function prototype) to insert an additional parameter into the member function to provide an access pipeline so that class object can call the function, which is called the This pointer:
Non-const nonstatic member Expansion process Point3D Point3d::magnitude (Point3D *const this)
If the member function is const, it becomes:
Const NONSTATIC member Expansion Process Point3D point3d::magnitude (const Point3D *const this)
2.Change each "Access operation to nonstatic data member" to be accessed via the this pointer:
{return sqrt (this->_x * this->_x + this->_y * this->_y + this->_z * this->_z;}
3.Rewrite the member function as an external function that "mangling" the function name so that it becomes a unique vocabulary in the program:
extern MAGNITUDE__7POINT3DFV (Register Point3D *const this);
Now this function has been converted, and every invocation operation must be converted. So:
Obj.magnitude ();
has become:
MAGNITUDE__7POINT3DFV (&obj);
and
Ptr->magnitude ();
has become:
MAGNITUDE__7POINT3DFV (PTR);
The normalize () function mentioned at the beginning of this chapter is converted to the following form, where it is assumed that a Point3D copy constructor has been declared and that the optimization of named returned value (NRV) has also been implemented:
The following describes the internal conversion of the "Named Return value Function"//using C + + pseudo-code void NORMALIZE_7POINT3DFV (Register const Point3D *const this, Point3D &_ _result) {Register float mag = this->magnitude ();//Default Constructor__result. Point3D::P Oint3d (); __result._x = this->x/mag;__result._y = This->y/mag;__result._z = This->z/mag;return;}
A more efficient approach is to directly construct the "normal" value, like this:
Point3D point3d::normalize () const {Register FLOAT mag = magnitude (); return Point3D (_x/mag, _y/mag, _z/mag);}
It will be converted to the following code:
The following describes internal conversions//using C + + pseudo code void NORMALIZE_7POINT3DFV (Register const Point3D *const this, Point3D &__result) {Register float Mag = This->magnitude ();//__result replaces the return value __result. Point3D::P Oint3d (This->_x/mag, This->_y/mag, This->_z/mag); return;}
This saves the additional burden caused by the default constructor initialization lock.
Special handling of names (name mangling)In general, member's name is preceded by a class name, creating a unique naming. For example, the following declaration:
Class Bar {public:int ival;};
Where Ival is likely to turn out like this:
Member one of the possible results after name-mangling Ival_3bar
Why does the compiler want to do this? Consider such a derivation operation (derivation):
Class Foo:public Bar {Public:int IVA;};
Remember, the Foo object incorporates both the base class and the derived class:
The internal description of C + + pseudo code//Foo class Foo {public:int ival_3bar;int ival_3foo;};
Regardless of which ival to process, through "name mangling" can be absolutely clear that, because member functions can be overloaded (overloaded), so the need for a broader mangling approach to provide an absolutely unique name, If you put:
Class Point {public:void x (float newx); float x ();};
Convert to:
Class Point {public:void x_5point (float newx); float x_5point ();};
Will cause two overloaded (overloaded) function entities to have the same name, in order to make them unique, only add their parameter list (which can be referenced from the function prototype). If you encode the parameter type, you can create a unique result, making two X () The function has a good conversion:
Class Point {public:void X_5POINTFF (float newx); float X_5POINTFV ();}
As shown above is only the encoding method used by Cfront, it must be admitted that the current compiler does not have a unified coding method.
By coding the parameters and function names together, the compiler then makes a finite form of type checking between different compiled modules, for example, if a print function is defined like this:
void print (const Point3D &) {...}
However, it is unexpectedly declared and invoked like this:
Assumed to be const POINT3D &void print (const POINT3D);
Two entities if you have a unique name mangling, any incorrect invocation operation fails at the link time due to an inability to make a resolution (resolved). It is sometimes optimistic to call this "link behavior to ensure type safety" (Type-safe linkage). Optimistically "is because it can only catch the token of the function (signature, which is the function name + number of arguments + parameter type) error, if the" return type "declaration error, there is no way to check it out.
In the current compilation system, there is a so-called demangling tool to intercept the name and convert it back.
Virtual Member Functions (dummy member function)If normalize () is a virtual member function, then the following call:
Ptr->normalize ();
will be converted internally into:
(*ptr->vptr[1]) (PTR);
which
Vptr represents a pointer generated by the compiler, pointing to Virtual table, which is inserted in each class object that declares (or inherits from) one or more virtual functions. In fact, its name will be "mangled", Because in a complex class derivation system, there may be multiple vptrs.
1 is the index value of the virtual table slot, associated to the normalize () function
A second PTR represents the this pointer.
Similarly, if magnitude () is also a virtual function, its invocation operation in normalize () is translated as follows:
Register Float mag = magnitude (); register float mag = (*this->vptr[2]) (this);
At this point, since Point3d::magnitude () is called in Point3d::normalize () and the latter has been resolved by the virtual machine mechanism (resolved), it is more efficient to explicitly invoke the "Point3D entity". and therefore suppress unnecessary repetitive invocation operations that result from virtual mechanisms:
The explicit invocation operation (explicity invocation) suppresses the virtual mechanism register FLOAT mag = point3d::magnitude ();
If the magnitude () declaration is more efficient with the inline function, using class scope operator explicitly invokes a virtual function whose resolution (resolved) will be nonstatic member function as:
Register Float mag = MAGNITUDE_7POINT3DFV (this);
For the following calls:
Point3D obj;obj.normalize ();
If the compiler converts it to:
(* obj.vptr[1]) (&obj);
Although the semantics are correct, it is not necessary. Recall objects that do not support polymorphic (polymorphism) (Section 1.3), so the function entity called by obj can only be point3d::normalize (). " Call a virtual function via a class object. This operation should always be resolved by the compiler like the General nonstatic member function (resolved):
NORMALIZE_7POINTDFV (&obj);
Another benefit of this optimization project is that an inline function entity of virtual function can be expanded (expanded), thus providing great benefits.
Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.
Various invocation methods of the C + + object Model--member (fourth chapter)