Standard C ++ does not have real Object-Oriented function pointers. This is a pity, because Object-Oriented function pointers (sometimes called delegates) have proved their value in other languages. In Delphi (Object-Oriented PASCAL Language), Object-Oriented function pointers are the basis for borland to build a VCL (visual component library. Recently, C # is pushing the concept of delegation to show the success of its language. For many applications, delegation simplifies the use of some design patterns built using loosely coupled objects (such as observer Mode Observer, Strategy Mode strategy, and state mode). Note: these models come from the design model: the basis for reusable object-oriented software written by four giants ). There is no doubt that Object-Oriented function pointers are also useful for Standard C ++.
C ++ does not have the concept of delegation. It only provides member function pointers. Most C ++ programmers have never used member function pointers, and they have good reasons. Because the member function pointer has a very strange syntax (such as-> *,. *), it is difficult for programmers to understand exactly what they mean. Besides, most of the tasks can be implemented in other ways. Here is a misunderstanding: in fact, it is much easier for the compiler to implement an appropriate delegate than to implement a member function pointer.
In this article, I will unveil the secrets of member function pointers. First, I will introduce the syntax and features of the member function pointer. Then I will explain how the member function pointer is implemented in a general compiler, and how the compiler can efficiently implement delegation. Finally, I will show how I use this little-known knowledge about member function pointers to implement highly efficient delegation in most C ++ compilers. For example, calling a single object delegate in Visual C ++ (6.0 or. NET or. NET 2003) requires only two lines of assembly code!
Function pointer
Let's take a look at the function pointer. In C, and even later in C ++, a function pointer pointing to a parameter with an int and a char * and returning a float value (tentative: my_func_ptr) it may be declared as follows:
Float (* my_func_ptr) (INT, char *);
// To make it more understandable, I stronugly recommend that you use a typedef.
// Things can get maid when
// The function pointer is a parameter to a function.
// The Declaration wowould then look like this:
Typedef float (* myfuncptrtype) (INT, char *);
Myfuncptrtype my_func_ptr;
Note that the types of function pointers for different parameter combinations are different. In Microsoft Vc, different function call protocols (calling conventions) may also lead to different function pointer types. These function call protocols include _ cdecl, _ stdcall, and _ fastcall. You can use the function pointer to point to a function float some_func (INT, char *) as follows *):
My_func_ptr = some_func;
When you want to call this function in the future, you can do this:
(* My_func_ptr) (7, "arbitrary string ");
The types of function pointers can be converted to each other. However, converting the function pointer to void * is not allowed. Other operations are not described here. A function pointer can be assigned a value of 0 to indicate that this is a null pointer. The function pointer can also perform a series of comparison operations (you can use the comparison operator (= ,! =, <,>, <=,> = )). You can determine whether a function pointer is a null pointer by converting = 0 or implicitly to bool. Interestingly, the function pointer can be used as a non-type template parameter. This is fundamentally different from the type parameter, which is also different from the overall non-type parameter. It is instantiated Based on the name, rather than based on the type or value. Name-based template parameters are not supported by any compiler, or even by all other templates that support some templates.
In C, function pointers are usually used as parameters of some library functions (such as qsort) and Windows function callbacks. Of course, function pointers also have many other applications. The implementation of function pointers is simple: they are code pointers, and they save the starting address of the functions in the assembly language. Different function pointer types exist only to ensure that the function uses the correct call protocol when called.
Member function pointer
In C ++ programs, most functions are member functions. That is to say, they are part of a class. It is not allowed to point to a member function using a common function pointer. The correct way is to use a member function pointer. A member function pointer pointing to a member function of the someclass class can be declared as follows (function parameters are the same as above ):
Float (someclass: * my_memfunc_ptr) (INT, char *);
// For const member functions, it's declared like this:
Float (someclass: * my_const_memfunc_ptr) (INT, char *) const;
Note that we used (: *) in the preceding statement, that is, someclass became part of the statement. Member function pointers have an annoying restriction: they can only be used to point to member functions in a class. Different parameter combinations use different types of member function pointers. In msvc, different function call protocols (including _ cdecl, _ stdcall, _ fastcall, and _ thiscall ,__ thiscall are default values. Interestingly, the __thiscall keyword is not described in the document. Sometimes, if you use it explicitly, you will get an error message indicating that the keyword is reserved for future use.) You also need to use different function pointer types. If you use a member function pointer, we recommend that you use typedef to avoid unnecessary obfuscation.
You can point to the function float someclass: some_member_func (INT, char *) as follows *):
My_memfunc_ptr = & someclass: some_member_func;
Most compilers (such as msvc) allow you to omit &, but some (such as GNU g ++) do not. Therefore, if the code you want to write has good portability, keep it &. To call a member function pointer, you must provide an instance of someclass, and you must use a special operator-> *. This operator has a low priority, so it should be enclosed in parentheses:
Someclass * x = new someclass;
(X-> * my_memfunc_ptr) (6, "another arbitrary parameter ");
// You can also use the. * operator if your class is on the stack.
Someclass y;
(Y. * my_memfunc_ptr) (15, "different parameters this time ");
Don't blame me for this syntax. It seems that C ++ designers are keen on punctuation.
To support member function pointers, C ++ adds three Special operators than C. : * Used to declare the pointer,-> * And. * used to call the function pointed to by the pointer. It seems that the obscure and rarely used features in this language have received great attention. (You can even reload the-> * operator, even though the reason why you did this has exceeded my imagination. I only know this usage .)
A member function pointer can be set to 0 and can provide = and! = Operation, but only between member function pointers of the same class. Any member function pointer can be compared with 0 to determine whether it is a null pointer. Unlike common function pointers, unequal comparison operators (<,>,<=, >=) cannot be used between member function pointers. Like a function pointer, a member function pointer can be used as a non-type template parameter (the compiler may need to patch it ).
Weird member function pointer
There are some strange points about member function pointers. First, you must use a common function pointer instead of a member function pointer pointing to a static function. (Therefore, the name "member function pointer" is a bit misleading: in fact, they are only "non-static member function pointers ".) Second, when processing a derived class, there will be a few surprises. For example, the following code (do not change the annotation part) will be compiled on msvc:
Class someclass {
Public:
Virtual void some_member_func (int x, char * P ){
Printf ("in someclass ");};
};
Class derivedclass: Public someclass {
Public:
// If you uncomment the next line, the code at line (*) will fail!
// Virtual void some_member_func (int x, char * P) {printf ("in derivedclass ");};
};
Int main (){
// Declare a member function pointer for someclass
Typedef void (someclass: * someclassmfp) (INT, char *);
Someclassmfp my_memfunc_ptr;
My_memfunc_ptr = & derivedclass: some_member_func; // ---- line (*)
}
It is strange that & derivedclass: some_member_func is a member function pointer of the someclass class, but it is not a member of the derivedclass class. (The behavior of Some compilers varies slightly. For example, for digital Mars C ++, & derivedclass: some_member_func is not defined in this case .) However, if the derivedclass Class re-implements the virtual function some_member_func, the above Code cannot be compiled because & derivedclass: some_member_func is now a member function pointer of the derivedclass class.
The conversion between member function pointers is an unusually dark area. In the C ++ standardization process, whether a member function pointer can be converted from a class to a member function pointer of its base class or derived class, there is a lot of debate about whether the conversion can be implemented between two irrelevant classes. However, when the Standards Committee makes a decision, some compiler developers already have their own implementations, and they give their own answers to these questions. According to standard 5.2.10/9, you can use reinterpret_cast to store a member function of another unrelated class in one member function pointer of a class. The result of executing this converted member function is unpredictable. The only thing you can do is convert the member function pointer to its original type (which class is the original class or which class is converted back ). I will continue to discuss this issue as it is a far cry from the actual compiler.
In some compilers, strange things even occur between the base class and the member function pointer of the derived class. When you use multi-inheritance, using reinterpret_cast to convert a member function pointer from a derived class to a base class may not be compiled, it depends on the sequence of the base class when your derived class is declared! Here is an example:
Class derived: Public base1, public base2 // case ()
Class derived2: Public base2, public base1 // case (B)
Typedef void (derived: * derived_mfp )();
Typedef void (derived2: * derived2_mfp )();
Typedef void (base1: * base1mfp )();
Typedef void (base2: * base2mfp )();
Derived_mfp X;
For case (a), static_cast <base1mfp> (x) can work, but static_cast <base2mfp> (x) will fail. However, case (B) is the opposite. You can only safely convert the member function pointer from the derived class to the first base class! You can give it a try. msvc will throw the c4407 warning message, while digital Mars C ++ will cause an error. In addition, neither compiler allows you to use reinterpret_cast instead of static_cast (the reason for not allowing this is different ). However, some compilers will not raise any objection no matter what you do. Be careful!
There is another interesting rule in the standard: You can declare a member function pointer before a class is defined. This will bring unexpected effects to Some compilers (we will discuss it later ). If possible, try to avoid this.
It is worth noting that, like member function pointers, the C ++ standard also provides member data pointers. They use the same operators, and some implementation problems are the same. They are used in the implementation of STL: stable_sort, but I don't know what other valuable uses they have.
Use of member function pointers
So far, I have probably convinced you that the member function pointer is a strange thing. But what are their purposes? I searched a lot on the Internet and found two main usage methods of member function pointers from the Code published on the Internet:
A. The example is used to show C ++ syntax to C ++ beginners.
B. Implement Delegation)
Of course there are also some trivial applications, such as the single-row function adaptation and boost library in STL (allowing you to use member functions to use standard algorithms ). In these cases, they are used during compilation. Generally, function pointers do not appear in compiled code. The most interesting application of member function pointers is to define complex interfaces. Some important things can be implemented in this way, but I have not found many such examples. Most of the time, this work can be done through more elegant virtual functions, or through reconstruction of problems. So far, the most famous application of member function pointers is in the frameworks of various applications. They constitute the core of the MFC message system.
When you use the message ing macro (such as on_command) of MFC, you actually provide an array containing the Message ID and member function pointer (specified as c1_target :: * member function pointer ). This is why the MFC class must be derived from the csf-target class to process messages. However, various message processing functions have different parameter lists (for example, the ondraw function uses CDC * as its first parameter). Therefore, the array must contain all types of member function pointers. How does MFC handle it? They use a terrible lease (hack) to put all possible member function pointers to
In a huge consortium, this can disrupt the common types of C ++ checks. (You can go to afximpl. h and javastarg. cpp to view the messagemapfunctions consortium, "bloody! ) Because MFC is such an important piece of code, in fact, all c ++ compilers support this lease.
During my search, I couldn't find many outstanding application examples about member function pointers except for the compilation application. Because of their complexity, they do not add much value to the language. In the end, it cannot escape the conclusion that the design of C ++ member function pointers is flawed.
When I write this article, I have a main point of view: the C ++ standard allows you to convert between member function pointers, but you are not allowed to call them after the conversion is successful, how ridiculous! It is ridiculous for the following three reasons. First, conversions do not always work on many popular compilers (that is, conversions are standard, but not portable ). Second, in all compilers, if the conversion is successful, the result of calling the converted member function pointer will meet your expectation: (standard) there is no need to classify it as "non-defined behavior ". (Pray? Is portable, but not standard !) Third, it is useless to allow conversion but not to allow a prayer (Invocation. If conversion and prayer are both possible, effective delegation is easy to implement. This will bring great value to the language.
A pointer is a variable pointing to some memory addresses. It can be a data address or a function address. C ++
The member pointer follows the same principle. The difficulty is that all pointers need an address, but there is no place inside the class.
Select a member of the class, which means the offset in the class. Only the starting address of the Offset and the specific object
To obtain the actual address. The syntax of the member pointer requires that a member be referenced reversely when an object is selected.
Pointer.
Struct simple {int ;}
Simple so;
Simple * sp = & so;
If there is a pointer to this structure SP and object so, if there is a pointer pointing to a Class Object member, or even
What happens if it represents a certain offset in the object? In order to obtain the content pointed to by the pointer, the * must be used for reverse reference. However, it is only an object offset, so you must specify that object. Therefore, the * number must be combined with the reverse referenced object.
SP-> * PM = 47; so. * PM = 47;
What is the syntax for defining PM? In fact, it is like any pointer and must specify the type it points. In addition, a '*' is also used in the definition. The only difference is that you must specify the class object used by the member pointer. Of course, this is implemented using the class name and global OPERATOR:
Define the member pointer:
Int simple: * PM;
Define and initialize the member pointer:
Int simple: * PM = & simple:;
Because it is referenced to a class rather than the object of that class, & simple: A can only be expressed as a member pointer syntax.
The pointer to the function is defined in the following format: int (* FP) (float); (* FP) parentheses are used to force the Compiler
Correct definition. No parentheses. This expression is a function that returns the int * value. Parentheses play an equally important role in defining and using a member function pointer. Assume that there is a function in a structure:
Struct simple2 {int F (float );};
You can insert a class name and a global operator into a common function to define a pointer to a member function:
INT (simple2: * FP) (float );
Initialization:
INT (simple2: * FP) (float) = & simple2: F;
& No. is optional. You can use a Function Identifier without a parameter table to indicate the address: fp = simple2: F;
Usage:
Simple2 S2;
Int I = (S2. * FP) (1.5 );
Another example
Class CB
{
Int F1 () {return 1 ;}
Int F2 () {return 2 ;}
INT (CB: * fptr [2]) ();
Public:
CB () {fptr [0] = CB: F1; fptr [1] = & CB: F2 ;}
Int sel (int I) {return (this-> * fptr [I]) ();}
};
In the constructor, the initialization of the member pointer seems to be excessively specified. Can I write like this:
Fptr [1] = F2; since the name F2 appears in the member function, can it be automatically considered within the scope of this class? The problem is that this does not conform to the syntax of the member function. The syntax requires the compiler to determine what to do. When a member function is referenced in reverse order, it is still excessively specified. This seems redundant. As mentioned above, when it is reversed, the syntax also requires that the member pointer always be bound with an object.
Boldly use pointers to member functions
Pointers to member functions are complex syntax structures in C ++. However, they are indispensable in event-driven and multi-threaded environments, especially when calling member functions from outside. In multiple threads, each thread calls this function by pointing to the member function pointer. If C ++ does not have these syntax features, it may be difficult to develop C ++ programs in many cases.
You may be intimidated by this syntax at first, but when you are familiar with it, you will find it quite convenient and can use itTypedefStatement to simplify. This section describes how to declare a pointer to a member function, assign a value to it, and call a function through this pointer.
Declare a pointer to a member function
A pointer to a member function includes the return type of the member function,::Class Name of the symbol, function parameter table. Although this syntax seems complicated, it is actually the same as a common pointer. The pointer to an external function can be declared as follows:
void (*pf)(char *, const char *);void strcpy(char * dest, const char * source);pf=strcpy;
The pointer to the member function of Class A is as follows:
void (A::*pmf)(char *, const char *);
AbovePMFIs a pointer to a member function of Class A, passing two variablesChar *AndConst char *, No return value. Note the: symbol before the asterisk, which is consistent with the preceding statement.
Assignment
To assign a value to a pointer to a member function, you can use the member function name and add an & method before it. for sample code, seeListing. Although some old compilers can ignore the Ampersand, they are not allowed in Standard C ++.
Use typedef
You can use typedef to hide complex pointers to member functions. For example, the following code defines the pointer of a member function in Class.PMAAnd passChar *AndConst char *Parameters.
Typedef void (A: * PMA) (char *, const char *); pma pmf = & A: strcat; // use a typedef to define a pointer to MemberUseTypedefIt is particularly useful, especially for Array pointers pointing to member functions.
Use pointers to call member functionsA pointer to a member function can call a member function of an object without the need to know the name of the function. For example, a sending function usesPMFTo call a function,
Whether this function pointsStrcpy ()OrStrcat ()Irrelevant. This is different from the traditional method of calling external methods through pointers. The traditional pointer call method is
You must specify the specific object of the called member function.
To clearly illustrate this point, let's look at an example. Assume that you have two class A objects, suchListing B. The pointer to the member function is a polymorphism.
In this way, if you use this pointer to call a virtual member function, this call is dynamic.
Advanced usageAfter mastering the basic usage, let's take a look at some advanced usage:
Array pointer to a member function
In the following example, I declare an array pointing to the two pointers of the member functions and assign them the address of the member functions of the class:
PMA pmf[2]={&A::strcpy, &A::strcat};
Such arrays are very useful in menu drivers, such as Table C.Listing c.
Fixed member functions
The Type pointing to a member function includes the name of the member function. The member function has a fixed/unfixed nature. As long as the member functions are variable,PMFWill point to any Member
Function. So, if you wantTouppercase ()Address AssignedPMF, The program will have an error, becauseTouppercase ()Is fixed,
Listing dShows the code of this example.
Although some compilers allow non-fixed pointers to a fixed member function, they are not allowed in Standard C ++.
ConclusionPointers to member functions are essentially complex data structures that contain multiple data members. At the beginning, you may think it is unmeasurable, but once you get used to it
With this syntax, you will feel that this method is essential in programming, especially when the function is frequently called in event-driven and multithreaded design.
The member function pointer is applied to the correspondence between message processing functions in MFC, and the member function of the derived class is called from the base class. The approximate method is as follows:
Class T;
Class T1;
Typedef void (T: * msg_func) (void );
Class t
{
Public:
T ()
{
}
Virtual ~ T ()
{
}
Void test1 (msg_func P)
{
(This-> * p )();
}
};
Class T1
{
Public:
T1 ()
{
}
Virtual ~ T1 ()
{
}
Void Test2 ()
{
Cout <"Test2 ";
}
};
Int main ()
{
T;
T. test1 (msg_func) & T1: Test2 );
Return 0;
}