The instance resolves the pointer to a member function of a class in C++

  • 2020-05-10 18:30:04
  • OfStack

Pointers in the C language are quite flexible and convenient, but they are also quite error-prone. Many beginners of C, or even old birds of C, can easily fall under the hands of C. But there is no denying that Pointers are extremely important in the C language, perhaps to the point that an C program without Pointers is not really an C program.
However, the C++ pointer always gives me a feeling of limited hands and feet. C++ has a stricter static typing than C, with a greater emphasis on type safety and compile-time checking. Therefore, for the most misused Pointers in C language, C++ Pointers are divided into data Pointers, data member Pointers, function Pointers, and member function Pointers, and cannot be randomly converted to each other. And none of these Pointers are declared in the same format:

数据指针 T *
成员数据指针 T::*
函数指针 R (*)(...)
成员函数指针 R (T::*)(...)

One more important difference is that the pointer takes up a different amount of space. Even in 32-bit systems, the amount of space can be 4, 8, 12, or even 16 bytes, depending on the platform and compiler.
Although C++ still has the universal pointer void*, it is the object of criticism and is no longer "universal". It cannot be converted to a member pointer.
In this way, the C++ pointer becomes awkward: we need a pointer that can point to the same type of data, whether it is normal data or member data. We need a pointer to a function of the same type, whether it's a static function or a member function. But no, at least not in the current C++ standard.
In programming work may happen in a "class" to invoke a member function through a function pointer in the request, such as, when used in a class the sort of C + + standard library functions qsort, because qsort parameters need to be a "comparison function pointer, if the" class "use one of the member function as a comparison function, you need to pass qsort for its call this member function pointer. The member functions that call "class" with Pointers discussed in this article include the following three cases:

(1). Assign a pointer to a non-member function of the same type to the member function of the "class", such as:

Example 1


#include <stdlib.h> 
typedef void (*Function1)( ); // define 1 Function pointer types.  
Function1 f1; 
class Test1 
{ 
 public:  
// ... The called member function.  
void Memberfun1( ){ printf("%s \n","Calling Test3::Memberfun2 OK");}; //  
void Memberfun2() 
{ 
 f1=reinterpret_cast<Function1>(Memberfun1);// Assigns a pointer to a member function f1 . Compile error.  
 f1(); 
} 
// ...  
}; 
int main() 
{ 
 Test1 t1; 
 t1.Memberfun2(); 
 return 0; 
} 

(2) in a "class", there are standard library functions, such as qsort, or other global functions, which call the member functions of the class with function Pointers. Such as:

Example 2:


#include <stdlib.h> 
class Test2 
{ 
private:  
int data[2];  
// ...  
public: 
// ...  
int __cdecl Compare(const void* elem1, const void* elem2) // Member function.  
{  
printf("%s \n","Calling Test2::Memberfun OK"); 
return *((int*)elem1)- *((int*)elem2) ;  
} 
void Memberfun()  
{  
data[0]=2; data[1]=5; 
qsort( data, 2, sizeof(int), Compare); // The standard library function is called  
// The member function. Compile error.  
} 
// ...  
}; 
int main( ) 
{ 
Test2 t2; 
t2.Memberfun(); // Call the member function.  
return 0; 
} 

(3) within the same "class", one member function calls another member function, such as:

Example 3:


#include "stdlib.h" 
class Test3 
{ 
public: 
// ...  
void Memberfun1( void (* f2)( ) ) { f2( ) ;} // A member function 1 Call a member function //2 .  
void Memberfun2( ) { printf("%s \n","Calling Test3::Memberfun2 OK");} // A member function 2 .  
void Memberfun3( ) { Memberfun1( Memberfun2);} //  Compile error   
// ...  
}; 
int main( ) 
{ 
Test3 t3; 
t3.Memberfun3(); // Call the member function.  
return 0; 
} 

There are no significant errors in the syntax of the code in the above three cases, and in some of the older compiled environments, such as VC++ 4.0, it usually compiles through, or at most gives a problem alert (Warning). Later compiler tools, such as VC++6.0 and some other commonly used C++ compiler software, could not compile the above code, and pointed out the error as follows (take VC++ 6.0 for example in the third case) :


error C2664: 'Memberfun1' : cannot convert parameter 1 from 'void (void)' to 'void (__cdecl *)(void)'
None of the functions with this name in scope match the target type

That is, the function called in the Memberfun1 parameter is of the wrong type.

As mentioned above, you can't eliminate the error by changing the type of the function. However, if you just take these functions out of the class definition, you can eliminate the error by compiling without making any changes. Take case 3 as an example, the following code can be compiled:


#include <stdlib.h> 
void Memberfun1( void (* f2)( ) ) { f2( ) ;} // Original member function 1 Call a member function //2 .  
void Memberfun2( ) { printf("%s \n","Calling Test3::Memberfun2 OK");} // Original member function 2 .  
void Memberfun3( ) { Memberfun1( Memberfun2);} 
int main( ) 
{ 
Memberfun3 (); 
return 0; 
} 

Cases 1 and 2 are exactly the same as cases 3.

It follows from this that the reason compilation fails in the three cases above is not ostensibly because the function type is called incorrectly, but because of the "class". The non-compiled case is that a member function of the "class" is called with a function pointer, while the compiled case is that a non-member function is called with a function pointer, and the type of the function is exactly the same. So, what's the difference between a pointer to a member function of a "class" and a pointer to a non-member function?

In the following program, the sizeof() function is used to view the length (size) of member and non-member function Pointers to various "classes" and output them to the screen.


#include "stdafx.h" 
#include <iostream> 
#include <typeinfo.h> 
class Test; //1 Two undefined classes.  
class Test2 //1 An empty class.  
{ 
}; 
class Test3 //1 It's a defined class.  
{ 
 public: 
//... 
void (* memberfun)(); 
void Memberfun1( void (* f2)( ) ) { f2( ) ;} // A member function 1 Call a member function //2 .  
void Memberfun2( );// A member function 2 .  
// ...  
}; 
class Test4: virtual Test3 ,Test2 //1 a virtual Inherited classes ( derivative class ).  
{ 
 public: 
void Memberfun1( void (* f2)( ) ) { f2( ) ;}  
}; 
class Test5: Test3,Test2 //1 Class ( derivative class ).  
{ 
 public: 
void Memberfun1( void (* f2)( ) ) { f2( ) ;}  
}; 
 
int main() 
{ 
 std::cout <<"1 General function pointer length = "<< sizeof(void(*)()) << '\n'; 
 std::cout <<"- The length of a pointer to a member function of a class -"<<'\n'<<'\n'; 
 std::cout <<"Test3 Class member function pointer length ="<< sizeof(void(Test3::*)())<<'\n'<<'\n'; 
 std::cout <<"Test5 Class member function pointer length ="<<sizeof(void (Test5:: *)())<<'\n'; 
 std::cout <<"Test4 Class member function pointer length ="<<sizeof(void (Test4:: *)())<<'\n'; 
 std::cout <<"Test Class member function pointer length ="<<sizeof(void(Test::*)()) <<'\n'; 
 return 0; 
} 

The output is (VC++6.0 compiled, running on Win98 operating system, other operating systems may be different) :

Pointer length of 1 general nonmember function = 4 - class member function pointer length - Pointer length of member function of class Test3 =4 Pointer length of member function of class Test5 =8 Pointer length of Test4 class member function =12 Pointer length of Test class member function =16

The above results show that the 32-bit Win98 operating systems, 1 as the length of a function pointer to 4 bytes (32), and the length of a class member function pointer as the class definition or not and the types of the inheritance, class relationship and inheritance from class (Test3) 4 bytes (32-bit) to have a virtual inheritance class (Virtual Inheritance) (Test4) 12 bytes (96), Only the class (Test) that says (declaration) is not defined (Test) because some information about it is not clear that the member function pointer has a maximum of 16 bytes (128 bits). Obviously, unlike 1 as a function pointer, pointer to member function of the "class" not only contains a member function of address information, and contains the information related to the attributes of a class, as a result, 1 a function pointer and class member function Pointers are two fundamentally different types, of course, also cannot directly with 1 as a function pointer call class member function, which is why this article mentioned at the beginning of three kinds of circumstances compilation errors. Although compiling with an earlier version of the compiled software can still pass, this leaves a serious hole in the program.

As to why the same is a pointer to the class member functions, its length was different, from 32 bits to 128 bits, difference is very big, because there is no see Microsoft's official data can only speculate VC + + 6.0 to class member function pointer at compile time is optimized, as far as possible to shorten the length of the pointer, after all, using 128 or 96 - bit pointer on a 32-bit operating system impact on program performance will be. However, no matter how optimized, it is certain that the member function pointer of the class contains 1 quantitative object (Objects) information. The reader can verify that other operating systems and compiler software do the same.

So, how do you call a member function of a class with a pointer when you need it? Consider the following:

(1) set the member function to be called as static type, such as: in example 2 above, static is added before the definition of class Test2 member function Compare as follows:


class Test2 
{ 
// ... . 
int static __cdecl Compare(const void* elem1, const void* elem2) // Member function.  
// The other is constant  
} 

The changed code compiles well. The reason is that the member function of static type is separated from the class, and its function pointer does not contain object information. Although this method is simple, it has two disadvantages: 1. No member of any class (including variables and functions) can appear in the definition of the function member being called; 2. 2. Due to the use of static members, classes are limited when they are inherited.

(2) a member function of static type with one function parameter containing object information is used to indirectly call other member functions. Taking example 3 as an example, the class Test3 is modified as follows, and the main () function remains unchanged, then it can be compiled smoothly:


class Test3 
{ 
 public: 
// ...  
void static __cdecl Helper(Test3* test3) 
{ 
 test3->Memberfun2(); 
} 
void Memberfun1( void (* f2)(Test3*)) { f2(this) ;} // Pass object information to Helper Function.  
void Memberfun2( ) {printf("%s \n","Calling Test3::Memberfun2 OK"); } // A member function 2 .  
void Memberfun3( ) { Memberfun1( Helper);}  
// ...  
}; 

This indirect approach has no restrictions on member functions, which overcomes the disadvantage of the first method that member functions cannot use members of any class, but class inheritance is still restricted due to the static members.

(3) a whole-process function (global function) is used as a member function of the intermediate indirect calling class. Take example 3 as an example, and the code is modified as follows (VC++6.0 is compiled and passed) :


class Test3; 
void __cdecl Helper(Test3* test3); 
class Test3 
{ 
 public: 
// ...  
void Memberfun1( void (* f2)(Test3*)) { f2(this) ;} // A member function 1 Call a member function //2 .  
void Memberfun2( ) {printf("%s \n","Calling Test3::Memberfun2 OK"); } // A member function 2 .  
void Memberfun3( ) { Memberfun1( Helper);}  
// ...  
}; 
 
void __cdecl Helper(Test3* test3) 
{ 
 test3->Memberfun2(); 
}; 

This method does not require any member functions, but requires more code.

In addition to the above three methods, there are other methods, such as modifying the code at the assembly level to solve the above problems, which are outside the scope of this article.

Conclusion: the function pointer cannot directly call the member function of the class, but needs to take an indirect method. The reason is that the member function pointer is fundamentally different from the 1 general function pointer. The member function pointer not only contains the address information, but also carries the information of the object it belongs to. This article provides three ways to call member functions indirectly. Each of the three methods has its advantages and disadvantages and is suitable for different occasions.


Related articles: