About calling class member functions with pointers in C ++ (1)

Source: Internet
Author: User
In programming, we often encounter the requirements for calling member functions through function pointers in a "class", such, when the sorting function qsort in the C ++ standard library is used in a class, because the qsort parameter requires a "comparison function" pointer, if this "class" uses a member function as a "comparison function", you need to pass the pointer of this member function to qsort for its call. The member functions used to call a class with pointers in this article include the following three situations:

(1) Assign the member function pointer of the class to the non-member function pointer of the same type, for example:

Example 1

# Include <stdlib. h>

Typedef void (* function1) (); // defines a function pointer type.
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); // assign the member function pointer to F1. Compilation 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, and the function pointer is used to call the member functions of the class. For example:

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 calls
// Member function. Compilation error.
}

//...
};

Int main ()
{
Test2 T2;
T2.memberfun (); // call a member function.
Return 0;

}

(3) Within the same "class", one member function calls another member function, for example:

Example 3:

# Include "stdlib. H"
Class test3
{
Public:
//...
Void memberfun1 (void (* F2) () {F2 () ;}// member function 1 calls member function // 2.
Void memberfun2 () {printf ("% s/n", "calling test3: memberfun2 OK");} // member function 2.
Void memberfun3 () {memberfun1 (memberfun2);} // compilation Error
//...
};

Int main ()
{

Test3 T3;
T3.memberfun3 (); // call a member function.
Return 0;

}

There are no significant errors in the code syntax in the above three cases. In some earlier compilation environments, such as VC ++ 4.0, the code can usually be compiled, or give a warning at most ). Later compilation tools, such as VC ++ 6.0 and some other commonly used C ++ compilation software, cannot be compiled through the above Code, the error is shown as follows (in the third case, VC ++ 6.0 is used as an example ):

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 type called in the memberfun1 parameter is incorrect.

As prompted above, errors cannot be eliminated only by changing the function type. However, if you extract these functions from the class definition, you can eliminate errors by compiling without making any changes, take the third case as an example. The following code can be compiled:

# Include <stdlib. h>

Void memberfun1 (void (* F2) () {F2 () ;}// the original member function 1 calls the 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;
}

1st and 2 are the same as 3rd.

It can be concluded that the above three situations cannot be compiled because the function type call is not correct, but related to the class. If the compilation fails, the function pointer is used to call the member functions of the "class". By compiling, the function pointer is used to call non-member functions, and the function types are identical. So, what are the differences between the member function pointer and the non-member function pointer of the "class?

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

# Include "stdafx. H"
# Include <iostream>
# Include <typeinfo. h>

Class test; // an undefined class.

Class Test2 // an empty class.
{
};

Class test3 // a defined class.
{
Public:
//...
Void (* memberfun )();
Void memberfun1 (void (* F2) () {F2 () ;}// member function 1 calls member function // 2.
Void memberfun2 (); // member function 2.
//...
};

Class test4: Virtual test3, Test2 // a class with virtual inheritance (derivative class ).
{
Public:
Void memberfun1 (void (* F2) () {F2 ();}
};

Class test5: test3, Test2 // an inherited class (derivative class ).
{
Public:
Void memberfun1 (void (* F2) () {F2 ();}
};

Int main ()
{
STD: cout <"General function pointer length =" <sizeof (void (*) () <'/N ';
STD: cout <"-class member function pointer length-" <'/N ';
STD: cout <"test3 member function pointer length =" <sizeof (void (test3 ::*)()) <'/N ';
STD: cout <"test5 class member function pointer length =" <sizeof (void (test5: *) () <'/N ';
STD: cout <"test4 member function pointer length =" <sizeof (void (test4: *) () <'/N ';
STD: cout <"test class member function pointer length =" <sizeof (void (test: *) () <'/N ';
Return 0;
}

The output result is (compiled by VC ++ 6.0 and runs on the Win98 operating system. Other operating systems may be different ):

Generally, the length of a non-member function pointer is 4.
  
-Class member function pointer length-

Test3 member function pointer length = 4
Test5 member function pointer length = 8
Test4 member function pointer length = 12
Test member function pointer length = 16

The above results show that in 32-bit Win98 operating system, the length of the function pointer is generally 4 bytes (32-bit ), the length of the member function pointer of the class changes with the definition of the class or the inheritance type and relationship of the class, from the 4-byte (32-bit) of the non-inheritance relationship class (test3) to the 12-byte (96-bit) of virtual inheritance (test4), only the class (TEST) not defined in Declaration) some information related to this function does not indicate that the member function pointer can be up to 16 bytes (128 bits ). Obviously, unlike the general function pointer, the pointer to the member function of the "class" contains not only the information of the member function address, but also information related to the class attributes. Therefore, generally, the function pointer and the member function pointer of the class are two different types. Of course, you cannot directly call the member function of the class using the general function pointer, this is why compilation fails in the three cases mentioned in this article. Although earlier versions of the compilation software can still be compiled, this poses a serious risk to the program.

As for why the pointer to the member function of the class is also different in length, from 32-bit to 128-bit, the difference is great, since I did not see Official Microsoft documents, I can only speculate that VC ++ 6.0 optimized the member function pointer of the class during compilation to shorten the pointer length as much as possible, after all, using a 128-bit or 96-bit pointer on a 32-bit operating system will affect program performance. However, no matter how optimized, the member function pointer of the class contains a certain amount of object (objects) information. If other operating systems and compilation software are processed Similarly, you can use the above programs to verify themselves.

So how do I use pointers to call member functions of a class when necessary? You can consider the following methods:

(1) set the member function to be called to the static type. For example, in Example 2 above, add the following static before the Compare definition of the class Test2 member function ():

Class Test2
{
//....
Int static _ cdecl compare (const void * elem1, const void * elem2) // member function.
// Others remain unchanged
}

The changed code is successfully compiled. The reason is that member functions of the static type are separated from classes, and their function pointers do not contain object information, which is consistent with general function pointers. This method is simple, but has two disadvantages: 1. The called function member definition cannot contain any class members (including variables and functions); 2. Because static members are used, the class is limited when it is inherited.

(2) Use a static member function that contains object information as a function parameter to call other member functions indirectly. For example, use example 3, make the following changes to the test3 class (where the English version is changed), and the main () function remains unchanged, so that compilation can be successful:

Class test3
{
Public:
//...
Void static _ cdecl helper (test3 * test3)
{
Test3-> memberfun2 ();
}
Void memberfun1 (void (* F2) (test3 *) {F2 (this);} // transmits the object information to the helper function.
Void memberfun2 () {printf ("% s/n", "calling test3: memberfun2 OK");} // member function 2.
Void memberfun3 () {memberfun1 (helper );}
//...
};

This indirect method has no limitations on member functions and overcomes the disadvantages that member functions cannot use any class members in the first method. However, due to static members, class inheritance is still restricted.

(3) Use a global function as a member function of the intermediate indirect call class. For example, use example 3 to modify the code as follows (compiled by VC ++ 6.0 ):

Class test3;
Void _ cdecl helper (test3 * test3 );

Class test3
{
Public:
//...
Void memberfun1 (void (* F2) (test3 *) {F2 (this);} // member function 1 calls member function // 2.
Void memberfun2 () {printf ("% s/n", "calling test3: memberfun2 OK");} // member function 2.
Void memberfun3 () {memberfun1 (helper );}
//...
};

Void _ cdecl helper (test3 * test3)
{
Test3-> memberfun2 ();
};

This method has no requirements for 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 is not within the scope of this article.

Conclusion: function pointers cannot directly call member functions of a class. Indirect methods are required because member function pointers are fundamentally different from common function pointers, in addition to the address information, the member function pointer carries the information of the object to which it belongs. This document provides three methods for indirectly calling member functions. These three methods have their own advantages and disadvantages and are suitable for different occasions.

 

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.