Deep Exploration C ++ object model Reading Notes (4)

Source: Internet
Author: User
Tags doxygen wxwidgets
Deep Exploration C ++ object model Reading Notes (4 ).

* ** Nonstatic member functions )***

One of the design principles of C ++ is that nonstatic member functions must have at least the same efficiency as general nonmember functions. That is to say, if we want to select between the following two functions:

 float magnitude3d(const Point3d *this) { ... }
float Point3d::magnitude3d() const { ... }

Therefore, choosing member function should not bring any extra burden. Because the compiler has converted the "member function entity" into a peering "nonmember function entity ". The following is a nonmember definition of magnqueue:

Float pointer3d: magnbench () const
{
Return SQRT (_ x * _ x + _ y * _ y + _ z * _ z );
}
// Internal conversion
Float magnitude_7point3dfv (const point3d * This) // The function name has been mangling
{
Return SQRT (this-> _ x * This-> _ x + this-> _ y * This-> _ y + this-> _ z * This-> _ z );
}

Now, every call operation to this function must be converted:

OBJ. magn.pdf ();
// Convert
Magnitude_7point3dfv (& OBJ );

For memeber in class, you only need to add the class name in the member name to form a unique name. However, because member functions can be reloaded, more extensive mangling techniques are required to provide unique names. One way is to encode the types of parameters in their parameter linked list.

Class Point {
Public:
Void X (float newx );
Float X ();
...
};
// Internal conversion
Class Point {
Void x_5pointff (float newx); // F indicates function, and F indicates that its first parameter type is float
Float x_5pointfv (); // v indicates that it has no Parameter
};

Keywords: malloc wxWidgets OpenGL polymorphism doxygen Deep Exploration C ++ object model Reading Notes (4 ).

The above mangling method can be used to check whether the call operation is correct during the link period. However, because the return type is not considered during encoding, if the return type declaration is incorrect, the result cannot be checked.

* ** Virtual member functions )***

For objects that do not support polymorphism and call a virtual function through a class object, this operation should always be determined by the compiler like a nonstatic member function:

// Point3d OBJ
OBJ. normalize ();
// Will not be converted
(* Obj. vptr [1]) (& OBJ );
// But will be converted
Normalize_7point3dfv (& OBJ );

* ** Static member functions )***

Before introducing static member functions, C ++ requires that all member functions be called through the object of this class. In fact, if no nonstatic data members is directly accessed, there is no need to call a member function through a class object. this leads to a conflict: on the one hand, it is a good habit to declare static data member as nonpublic, however, this also requires that it provide one or more member functions to access this member. On the other hand, although you can access a static member without using class object, however, its access function must be bound to a class object.

Static member functions came into being in this situation.

Compiler developers support static member functions at the compilation and language layers:

(1) Compilation level: when the class designer wants to support the situation that "no class object exists", he can forcibly convert 0 to a class pointer, thus providing a this pointer entity:

// Internal conversion of function calls
Object_count (point3d *) 0 );

Keywords: malloc wxWidgets OpenGL polymorphism doxygen Deep Exploration C ++ object model Reading Notes (4 ).

(2) Language layer: the biggest feature of static member function is that it does not have this pointer. If you get the address of a static member function, the obtained location is in the memory, its address type is not a "pointer to class member function", but a "nonmember function pointer ":

Unsigned int point3d: object_count () {return _ object_count ;}
& Point3d: object_count ();
// An address is obtained. Its type is not
Unsigned int (point3d ::*)();
//
Unsigned int (*)();

Static member functions are often used as Callback functions.

* ** Virtual member functions )***

For call operations like PTR-> Z (), some relevant information of PTR during the execution period will be required. In order to make it possible to find and call the appropriate entity of Z () smoothly and efficiently during the execution period, we want to add some additional information to the object.

(1) A string or number, indicating the class type;

(2) A pointer pointing to a table with the execution period address of the virtual functions of the program;

In C ++, virtual functions can be known during compilation. Because the size and content of the table are not changed during program execution, therefore, the construction and access of this table can be fully controlled by the compiler without any intervention during the execution period.

(3) In order to find the table, each class object is inserted with a pointer generated by the compiler, pointing to the table;

(4) in order to find the function address, each virtual function is assigned a table index value.

A class has only one virtual table, which contains the addresses of all the active virtual functions in its corresponding class object, including:

(A) The function entity defined by this class

It will rewrite a possible base class virtual function entity. If no corresponding function exists in the base class, the corresponding slot will be added to the virtual table of the derived class.

Keywords: malloc wxWidgets OpenGL polymorphism doxygen Deep Exploration C ++ object model Reading Notes (4 ).

(B) function entities inherited from base class

This occurs only when the derived class decides not to rewrite the virtual function. Specifically, the address of the function entity in the base class will be copied to the slot corresponding to the virtual table of the derived class.

(C) pure_virtual_called function entity

For such a sub-statement:

 ptr->z();

After using the above method, although I don't know which Z () function entity will be called, I know every Z () all functions are placed in slot 4 (Here we assume that Z () in the base class is the fourth declared virtual function ).

// Internal conversion
(* PTR-> vptr [4]) (PTR );

* ** Virtual functions under multiple inheritance ***

Multi-inheritance supports Virtual functions. The complexity of these functions is centered on the second and subsequent base classes, and the "this pointer must be adjusted during the execution period.

Multiple inheritance issues:

(1) Call the derived class virtual function by pointing to the "second or subsequent base class" pointer (or reference). The call operation is associated with the "necessary this Pointer Adjustment" operation, must be completed during the execution period;

The following inheritance system is used as an example:

 class Base1 {
public:
Base1();
virtual ~Base1();
virtual void speakClearly();
virtual Base1 *clone() const;
protected:
float data_Base1;
};

class Base2 {
public:
Base2();
virtual ~Base2();
virtual void mumble();
virtual Base2 *clone() const;
protected:
float data_Base2;
};

class Derived : public Base1, public Base2 {
public:
Derived();
virtual ~Derived();
virtual Derived *clone() const;
protected:
float data_Derived;
};

Keywords: malloc wxWidgets OpenGL polymorphism doxygen Deep Exploration C ++ object model Reading Notes (4 ).

For the following line:

 Base2 *pbase2 = new Derived;

Will be converted:

// Transfer to support the second base class
Derived * temp = new derived;
Base2 * pbase2 = temp? Temp + sizeof (base1): 0;

Without such adjustment, any "non-polymorphism application" of the pointer will fail:

 pbase2->data_Base2;

When the programmer wants to delete the objects referred to by pbase2:

// The correct virtual destructor function entity must be called.
// Pbase2 needs to be adjusted to indicate the starting point of the complete object
Delete pbase2;

The pointer must be adjusted again to point to the beginning of the derived object. However, the preceding offset addition cannot be set directly during the compilation period, because the real object referred to by pbase2 can be determined only during the execution period.

Since then, we have understood the unique problem in Multi-inheritance: calling the derived class virtual function by pointing to the second or subsequent base class pointer (or reference, the "necessary this Pointer Adjustment" Operation associated with this call must be completed during the execution period. There are two ways to solve this problem:

(A) Increase the virtual table. Each virtual table slot is not just a pointer, but a combination, containing possible offset and address. In this way, the call operation of the virtual function changes:

(* Pbase2-> vptr [1]) (pbase2 );
// Change
(* Pbase2-> vptr [1]. faddr) (pbase2 + pbase2-> vptr [1]. offset );

The disadvantage of this practice is that it is equivalent to penalizing all virtual function call operations, whether or not they need offset adjustment.

(B) using the so-called thunk (a small assembly code), it has done the following two aspects:

Keywords: malloc wxWidgets OpenGL polymorphism doxygen Deep Exploration C ++ object model Reading Notes (4 ).

(1) adjust this pointer with an appropriate offset value;

(2) Jump to the virtual function.

 pbase2_dtor_thunk:
this += sizeof(base1);
Derived::~Derived(this);

The thunk technology allows the virtual table slot to contain a simple pointer. The address in the slot can direct directly to the virtual function or a related thunk. therefore, for virtual functions that do not need to adjust this pointer, there is no extra load on the carrying efficiency.

(2) due to two different possibilities:

(A) Called by derived class (or the first base class;

(B) It is called through the second (or its successor) base class. Multiple corresponding slots may be required for the same function in virtual table;

 Base1 *pbase1 = new Derived;
Base2 *pbase2 = new Derived;

delete pbase1;
delete pbase2;

Although two delete operations lead to the same derived destructor, they need two different virtual table slots:

(A) pbase1 does not need to adjust this pointer, and its virtual table slot must be placed with the real destructor address.

(B) pbase2 needs to adjust this pointer, and its virtual table slot needs the related thunk address.

The specific solution is:

Under Multi-inheritance, a derived class contains n-1 additional virtual tables, and N indicates the number of base classes on the previous layer. In this way, derived contains the following two tables: vtbl_derived and vtbl_base2_derived.

(3) The type of the returned value of a virtual function may be changed, which may be base type or publicly derived type. This can be explained through the derived: Clone () function entity.

 Base2 *pb1 = new Derived;

// Call derived: Clone ()
// The return value must be adjusted to point to base2 subobject
Base2 * PBS = PB1-> clone ();

Keywords: malloc wxWidgets OpenGL polymorphism doxygen Deep Exploration C ++ object model Reading Notes (4 ).

When you run PB1-> clone (), PB1 will be adjusted to the starting address of the derived object, so the clone () derived version will be called: it will return a pointer, point to a new derived object. The address of this object must be adjusted before it is assigned to master. when a function is considered to be "small enough", the sun compiler will provide a so-called "split functions" technology: two functions are generated using the same algorithm, and the second one is before the return, add the necessary offset to the pointer. Therefore, no matter the base1 pointer or derived pointer is used to call a function, the return value does not need to be adjusted. The base2 pointer calls another function.

* ** Virtual functions under virtual inheritance ***

Its internal mechanism is too strange, So I skipped it here. The only suggestion is: do not declare nonstatic data members in a virtual base class.

* ** Function efficiency ***

Since the nonmember, static member, and nonstatic member functions are converted to the same form, the efficiency and security of the three functions are the same. The efficiency of Virtual Member is significantly lower than the first three. There are two reasons: (a) vptr setting in the constructor; (B) offset difference model.

* ** Pointer to member function ***

Take the address of a nonstatic member function. If the function is nonvirtual, the result is the real address in the memory.

We can define and initialize the pointer as follows:

 double (Point::*coord)() = &Point::x;

To call it, you can do this:

 (origin.*coord)();
 (ptr->*coord)();

The Pointer "pointing to virtual member functions" will bring new problems. Pay attention to the following program snippets:

 float (Point::*pmf)() = &Point::z;
Point *ptr = new Point3d;

Keywords: malloc wxWidgets OpenGL polymorphism doxygen Deep Exploration C ++ object model Reading Notes (4 ).

PMF is a pointer to the member function. It is set to the address of point: Z () (a virtual function), and PTR is directed to a point3d object.

If we call z () directly via PTR ():

PTR-> Z (); // call point3d: Z ()

However, if we call z () indirectly via PMF ():

(PTR-> * PMF) (); // still calling point3d: Z ()

That is to say, the virtual mechanism can still run when "pointer to member function" is used. But how can this problem be solved?

Obtain the address of a nonstatic member function in the memory, and the address of a virtual member function, all we can get is the index value of the virtual function in its related virtual table. Therefore, calling Z () through PMF will be converted internally into the following form:

 (*ptr->vptr[(int)pmf])(ptr);

But how can we determine whether the function pointer passed to PMF is directed to the memory address or the index value in the virtual table? For example, the following two functions can be specified to PMF:

// Both can be specified to PMF
Float Point: X () {return _ x;} // nonvirtual function, representing the memory address
Float Point: Z () {return 0;} // virtual function, representing the index value in virtual table

Cfront 2.0 is determined by determining the size of the value (this implementation technique must assume that there are at most 128 virtual functions in the inheritance system ).

To allow pointers to member functions to support multiple inheritance and virtual inheritance, stroustrup has designed the following struct:

// Used to support pointers to member functions under multiple inheritance
Struct _ mptr {
Int delta;
Int index;
Union {
Ptrtofunc faddr;
Int v_offset;
};
};

Keywords: malloc wxWidgets OpenGL polymorphism doxygen Deep Exploration C ++ object model Reading Notes (4 ).

Here, index indicates the virtual table index, and faddr indicates the nonvirtual member function address (when the index does not point to the virtual table, it is set to-1 ).

Under this model, the following call operations are converted:

(PTR-> * PMF )();
// Internal conversion
(PMF. index <0)
? (* PMF. faddr) (PTR) // nonvirtual Invocation
: (* PTR-> vptr [PMF. Index] (PTR) // virtual Invocation

For the following function calls:

(Pa. * PMF) (PB); // Pa and Pb are point3d objects.

Will be converted:

 pmf.iindex < 0
? (*pmf.faddr)(&pA + pmf.delta, pB)
: (*pA._vptr_Point3d[pmf.index].faddr)(&pA + pA._vptr_Point3d[pmf.index] + delta, pB);

* ** Inline functions ***

During Inline expansion, each form parameter is replaced by the corresponding actual parameter. However, it should be noted that this replacement is not simply a one-to-one replacement (because it will lead to multiple evaluate operations on the actual parameters), and usually a temporary object needs to be introduced. In other words, if the actual parameter is a constant expression, we can complete the evaluate operation before the replacement; the subsequent inline replacement can directly bind the constant.

For example, suppose we have the following simple inline functions:

 inline int min(int i, int j)
{
return i < j ? i : j;
}

For the following three inline function calls:

 minval = min(val1,val2);
minval = min(1024,2048);
minval = min(foo(),bar()+1);

Will be expanded:

Minval = val1 <val2? Val1: val2; // directly replace the Parameter
Minval = 1024; // use a constant directly after replacement
Int T1;
Int T2;
Minval = (T1 = Foo (), (t2 = bar () + 1), t1 <t2? T1: T2; // there are side effects, so import temporary objects

Keywords: malloc wxWidgets OpenGL polymorphism doxygen Deep Exploration C ++ object model Reading Notes (4 ).

Local variables in the inline function can also generate a large number of temporary objects.

 inline int min(int i, int j)
{
int minval = i < j ? i : j;
return minval;
}

The following expression:

 minval = min(val1, val2);

Will be converted:

 int _min_lv_minval;
minval = (_min_lv_minval = val1 < val2 ? val1 : val2),_min_lv_minval;

All in all, the local variables in the inline function, coupled with parameters with side effects, may lead to the generation of a large number of temporary objects. Especially if it is extended multiple times with a single expression. The address of the new derived object must be adjusted to point to its base2 subobject.

Series of articles:

Deep Exploration C ++ object model Reading Notes (1)

Deep Exploration C ++ object model Reading Notes (2)

Deep Exploration C ++ object model Reading Notes (3)

Deep Exploration C ++ object model Reading Notes (5)

Deep Exploration C ++ object model Reading Notes (6)

Deep Exploration C ++ object model Reading Notes (7)

Last reading note of Deep Exploration C ++ Object Model

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.