4.4 Pointer to Member function (Pointer-to-member Functions)
take a nonstatic data member address, the result is the member in the class layout of the byte position (plus 1), it is an incomplete value, need to be bound to the address of a class object, can be accessed.
take the address of a nonstatic member function, assuming that the function is nonvirtual, the result is that it is in memory the true address. However, this value is not complete, it also needs to be bound to the address of a class object, This function can only be called by it, and all nonstatic member functions require the address of the object (as indicated in the reference).
A pointer to the member function whose declaration syntax is as follows:
double//return type (point::*//Class The function was member PMF)//name of the pointer to member ();//Argument list
You can then define and initialize the pointer like this:
Double (point::* coord) () = &Point::x;
You can also specify values like this:
Coord = &Point::y;
To invoke it, to do so:
(Origin.*coord) ();
Or
(Ptr->*coord) ();
These actions are translated by the compiler to:
(coord) (&origin);
And
(coord) (PTR);
The declaration syntax of a pointer to the member function, and a pointer to the member selection operator, which acts as the space reservation for the this pointer . It's AlsoThe
reason why the static member function (without this pointer) is a "function pointer" and not "pointer to member function".
using a "member function pointer" is not more expensive than using a "nonmember function pointer" If it is not used for virtual function, multiple inheritance, or virtual base class. .The three cases above are too complex for the type and invocation of the member function pointer. In fact, for those who do not have virtual functions or virtual base class, or multiple base class, the Translators can provide them with the same efficiency. The next section discusses why virtual function appears, making the "member function pointer" more complicated.
Pointers to "virtual Member Functions" supportedNote the following program fragments:
Float (point::* PMF) () = &Point::z; Point *ptr = new Point3D;
PMF, a pointer to the member function, is set to the address of Point::z () (a virtual function), and PTR is specified with a Point3D object, assuming that Z () is called directly through PTR:
Ptr->z ();
is called Point3d::z (), but assuming that Z () is called indirectly from PMF?
(PTR->PMF) ();
is the point3d::z () still called?
The virtual mechanism can still be shipped with the "pointer to member function"Is that OK? The answer is yes, and how does the problem happen?
For a nonstatic member function to take its address, it will get the address of the functions in memory, but in the face of a virtual function, its address is unknown at compile time, can only be the virtual function in its related The index value in virtual table. That is, for a virtual member function to take its address, you can only get an index value.
For example, if you have the following point declaration:
Class Point {public:virtual ~point (); float x (); float y (); virtual float z ();};
However get the address of destructor:
&Point::~Point;
The result is 1, taking the address of X () or Y ():
&point::x (); &point::y ();
The result is the address of the function in memory, because they are not virtual, take the address of Z ():
&point::z ();
The result is 2, which calls Z () via PMF, which is converted internally into a compile-time formula, such as the following:
(*ptr->vptr[(int) PMF]) (PTR);
Evaluating the evaluation of a pointer to member function (evaluted) is complicated by two meanings of the value, and its invocation will be different from the normal call operation. The internal definition of PMF, namely:
Float (point::* PMF) ();
You must agree that the function can address nonvirtual x () and Virtual Z () two member functions, which have the same prototype:
Both can be assigned to Pmffloat Point::x () {return _x;} Float Point::z () {return 0;}
is just
one represents the memory address and one that represents the index value in virtual table. Therefore, the compiler must define PMF so that it can (1) have two more values, (2) It is more important that its values can be differentiated to represent the memory address or the index value in virtual table.
Under multiple inheritance, a pointer to member functions
to allow pointers to member functions to support multiple inheritance and virtual inheritance, Stroustrup designs one of the following structures:
General structure to support pointers to member functions under multiple inheritance struct __mptr {int delta;int index;union {ptrtofunc faddr;int v_offset;};};
What do they have to show?Index
and FADDR are respectively (at different times) with the virtual table index and the nonvirtual member function address.In this model, like this call operation:
(PTR->*PMF) ();
will become:
(Pmf.index < 0)?//Non-virtual Invocation (*PMF.FADDR) (PTR)://Virtual Invocation (*ptr->vptr[pmf.index] (PTR));
The criticism of such a method is that each invocation operation has to pay the above cost to check whether it is virtual or nonvirtual. Microsoft took the check off and imported a so-called Vcall thunk. Under this strategy, the FADDR is either a true member function address (assuming the functions are nonvirtual) or Vcall The address of the thunk. So the virtual or novirtual function call operation is transparent, and Vcall Thunk selects and invokes the appropriate slot in the related virtual table.
Another side effect of this structure is that when a pointer to a constant value is passed to the member function, it needs to produce a transient object. For example, suppose to do this:
extern Point3D foo (const point3ed&, Point3D (Point3D::*) ()), void Bar (const point3d& p) {Point3D pt = foo (p, &po Int3d::normal);}
The value of &point3d::normal is similar to this:
{0,-1, 10727417}
will need to produce a temporary object with a clear initial value:
__mptr temp = {0,-1, 10727417}foo (P, temp);
At the beginning of this section of the struct, the Delta field represents the offset value of the this pointer. The V_offset field places the vptr position of the base class with the second or successor of virtual (or multiple inheritance). Suppose vptr is placed in the class by the compiler At the beginning of the object, this field is not necessary at the cost of reducing the compatibility of C objects. These fields are only necessary in the case of multiple inheritance or virtual inheritance, and many compilers provide multiple pointers to member functions within themselves based on different class attributes. Microsoft, for example, offers three flavors:
1. A single instance of inheritance (with Vcall thunk address or function address)
2. A multiple-inheritance instance (with Faddr and Delta two members)
3. A virtual inheritance instance (with four members)
"Pointer to member functions" efficiencyIn the following set of tests, the cross_product () function is called in the following way:
1. A pointer to the nonmember function
2. A pointer to the class member function
3. A pointer to virtual member function
4. Nonvirtual and virtual member function call under multiple inheritance
5. Nonvirtual and virtual member function call under dummy inheritance.
The first test (a pointer to the nonmember function) is listed in the following manner:
point3d* (*PF) (const point3d&, const Point3D &) = cross_product;for (int iters = 0; iters < 10000000; iters++) (*PF) (PA, PB); return 0;
A second Test
the Declaration and invocation operations (pointers to member function) such as the following:
point3d* (Point3D::* PMF) (const Point3D &) const = &point3d::cross_product;for (int iters = 0; iters < 10000000; iters++) (PA.*PMF) (PB);
The above operation is converted to the following internal form, and the following function is called:
(PA.*PMF) (PB);
will be translated into this inference:
Pmf.index < 0? (*PMF.FADDR) (&pa + Pmf.delta, pb): (*PA.__VPTR__POINT3D[PMF.INDEX].FADDR) (&pa + Pa.__vptr__point3d[pmf.index].delta, pb);
A pointer to member function is a structure that contains three fields: Index,faddr and Delta.index are not the index values of a related virtual table, or 1 means that the function is Nonvirtual.faddr the address with nonvirtual member function. The delta has a possible this pointer adjustment.
C + + object model--pointer to Member function (Pointer-to-member Functions) (fourth chapter)