[Z] A deep pointer from a class member function

Source: Internet
Author: User

Http://blog.csdn.net/hairetz/article/details/4153252

 

First look at this piece of code

 

Class Test
{
Public:
Test (int I) {m_ I = I ;}
Test (){};
Void Hello ()
{
Printf ("Hello/N ");
}
PRIVATE:
Int m_ I;
};

Int main ()
{
Test * P = new test ();
P-> Hello ();
P = NULL;
P-> Hello ();
}

 

The result is:

Hello

Hello

 

Why

P = NULL;
P-> Hello (); then, null-> Hello () is still valid?

 

Our first response must be that the member functions of the instance of the class, different from the member variables, all member functions exist in the same place, so when the instance of the class is called, it must be to call the same function pointer. (It makes sense to think about it. member functions have the same functions and do not need to be instantiated)

So we changed the code to this.

 

Class Test
{
Public:
Test (int I) {m_ I = I ;}
Test (){}
Void Hello ()
{
Printf ("Hello/N ");
}
PRIVATE:
Int m_ I;
};
Typedef void (test: * hello_func )();

Int main ()
{
Test * P = new test ();
Test Q;
P-> Hello ();
Hello_func phello_fun = & test: Hello;
Printf ("% P/N", phello_fun );
P = NULL;
Phello_fun = & test: Hello;
Printf ("% P/N", phello_fun );
Phello_fun = p-> hello;
Printf ("% P/N", phello_fun );
Phello_fun = Q. Hello;
Printf ("% P/N", phello_fun );
P-> Hello ();
}

 

The result is:

Hello
00401005
00401005
00401005
00401005
Hello
Press any key to continue

 

That is to say whether it is & test: Hello, p-> hello, or Q. Hello, or even null-> hello.

The call addresses are all 0x00401005, which basically proves our conjecture.

 

Is it all done here? No.

Someone asked the following code:

Ssvector & ssvector: assign2product4setup (const svset & A, const ssvector & X)
{
Int ret_val = pthread_create (& PT, null, (void (*) (void *) ssvector: prefun, X );
}

Void * ssvector: prefun (void * Arg ){
Const ssvector & Tx = * (ssvector *) Arg );
}
Green Line Error: Invalid conversion from 'void (*) (void *) 'to 'void * (*) (void *)'

Pthread_create I will not explain. The first parameter is the pointer of the thread function. Why is an error reported here?

 

It indicates the pointer of a common class member function (if it has a function pointer), which is different from a general function pointer.

 

Let's take a look at the analysis in the following article:

 

In the csdn forum, I often see some questions about class member function pointers. At first, I didn't care about them. I thought the member function pointers are the same as common function pointers, there is nothing to discuss about. After reading the relevant books, I suddenly realized that my previous understanding of member function pointers was too naive and superficial. It was not as simple as I thought before, it is not as "unknown" as I previously thought ". I am eager to learn more about member functions.

I. Theory
Before in-depth study and analysis, let's take a look at how the member functions are introduced in the book. To sum up the content of the class member function pointer, it should include the following knowledge points:
1. The member function pointer is not a common function pointer.
2. The compiler provides several new operators to support member function pointer operations:

1) operator ": *" is used to declare a class member function pointer, for example:
Typedef void (base: * pvvbasememfunc) (void); // base is a class
2) The operator "-> *" is used to call class member function pointers through object pointers. For example:
// Pbase is a base pointer and well initialized
// Pvibasememfunc is a member function pointer and well initialized
(Pbase-> * pvibasememfunc )();
3) The operator ". *" is used to call class member function pointers through objects. For example:
// Baseobj is a base object
// Pvibasememfunc is a member function pointer and well initialized
(Baseobj. * pvibasememfunc )();

3. Member function pointers are strongly typed.

Typedef void (base: * pvvbasememfunc) (void );
Typedef void (derived: * pvvderivememfunc) (void );
Pvvbasememfunc and pvvderivememfunc are two different member function pointer types.

4. Since member function pointers are not really pointers, conversion of member function pointers is limited. The specific conversion details depend on different compilers, or even different versions of the same compiler. However, different override functions and virtual functions between different classes in the same inheritance chain can still be converted.

Void * pvoid = reinterpret_cast <void *> (pvibasememfunc); // Error
Int * pint = reinterpret_cast <int *> (pvibasememfunc); // Error
Pviderivememfunc = static_cast <pviderivememfunc> (pvibasememfunc); // OK

II. Practice
With the above theoretical knowledge, we have a rough understanding of class member function pointers, but we still have too many questions about member function pointers. Since the member function pointer is not a pointer, what is it? Why does the compiler limit the conversion of member function pointers? In the old way, we still analyze and assemble the code to reveal the secrets. First, I wrote two classes with inheritance relationships:
Then, I defined some member function pointer types:
Finally, some test code is written in the main function:
Compile the compilation code. Old Rules: In the process of analyzing the assembly code, we only analyze the assembly code that makes sense to solve the problem. Others are temporarily ignored.
1. The member function pointer is not a pointer. From the code, we can see that in the calling stack (calling stack) of the main function, four member function pointers are first pushed in sequence. If they are common pointers, the offset between them should be four bytes, however, the actual situation is as follows:

 

 

"The Implementation of the pointer to member function must store within itself information as to whether the member function to which it refers is virtual or nonvirtual, information about where to find the appropriate virtual function table pointer (see the compiler puts stuff in classes [11, 37]), an offset to be added to or subtracted from the function's this pointer (see meaning of pointer comparison [28, 97]), and possibly other information. A pointer to member function is commonly implemented as a small structure that contains this information, although extends Other implementations are also in use. dereferencing and calling a pointer to member function usually involves examining the stored information and conditionally executing the appropriate virtual or nonvirtual function calling sequence."

2. Conversion of member function pointers. The code used in this article is to compare the differences between common member function pointers and virtual function pointers in the conversion process:
For the symbol "?? _ 9 @ $ b3ae ", I found the assembly code again: we can see that for virtual functions, even if the member function pointer is used for indirect calls, it still has the same features as direct calls.

 

 

; Pvibasememfunc = & base: setvalue;
MoV dword ptr _ pvibasememfunc $ [EBP], offset flat :? Setvalue @ base @ qaexh @ Z;
Extract the address of the base: setvalue function, which is stored in the first 4 bytes (DWORD) of memory occupied by the variable pvibasemfunc.

 

; Pvvbasememfunc = & base: foobar;
MoV dword ptr _ pvvbasememfunc $ [EBP], offset flat :?? _ 9 @ $ b3ae; 'vcall'
Retrieve the symbol "?? The value of _ 9 @ $ b3ae is stored in the first four bytes (DWORD) of the memory occupied by the variable pvvbasemfunc.

 

_ Text Segment
?? _ 9 @ $ b3ae proc near; 'vcall', comdat
MoV eax, dword ptr [ECx]
Jmp dword ptr [eax + 4]
?? _ 9 @ $ b3ae endp; 'vcall'
_ Text ends
Symbol "?? _ 9 @ $ b3ae "indicates a stub function. This function first obtains the pointer of the virtual function table based on the this pointer, and then redirects the instruction to the address of the corresponding virtual function.

 

; Pviderivememfunc = static_cast <pviderivememfunc> (pvibasemfunc );
MoV eax, dword ptr _ pvibasememfunc $ [EBP]
MoV dword ptr _ pviderivememfunc $ [EBP], eax
The value of DWORD In the first 4 bytes of memory occupied by variable pvibasememfunc is directly paid to the first 4 bytes of memory occupied by Variable _ pviderivemfunc.

 

; Pvvderivememfunc = static_cast <pvvderivememfunc> (pvvbasemfunc );
MoV eax, dword ptr _ pvvbasememfunc $ [EBP]
MoV dword ptr _ pvvderivememfunc $ [EBP], eax
The value of the first 4 bytes (DWORD) of the memory occupied by the variable pvvbasemfunc is directly paid to the first 4 bytes of the memory occupied by the variable pvvderivemfunc.

It can be seen that the member function pointer of the base class is converted to the member function pointer of the corresponding derived class, and the value remains unchanged. Of course, the inheritance relationships here are relatively simple. If there are multiple inheritance and virtual inheritance, the results may be more complex.

3. Function call
The following function calls are similar, and one of them is listed here: the assembly code here does not give us much fresh content: Put the first address of the object (this pointer) stored in the ECX register, and then the instruction is transferred to the address indicated by the first four bytes of memory occupied by Variable _ pvibasememfunc.

Here, we should have a better understanding of member function pointers.

; (Baseobj. * pvibasememfunc) (10 );
MoV ESI, ESP
Push 10; 0000000ah
Lea ECx, dword ptr _ baseobj $ [EBP]
Call dword ptr _ pvibasememfunc $ [EBP]
Cmp esi, ESP
Call _ rtc_checkesp

 

It can be seen that the offset between them is 12 bytes. These 12 bytes should contain three pointers, one of which should point to the address of the function. What about the other two pointers pointing? In chapter 16th of the book C ++ common knowledge: essential intermediate programming, the offset of the 12 bytes exactly confirms the content in the book:

 

Class base {
Public:
// Ordinary member function
Void setvalue (INT ivalue );

// Virtual member function
Virtual void dumpme ();
Virtual void foobar ();

Protected:
Int m_ivalue;
};

Class derived: public base {
Public:
// Ordinary member function
Void setvalue (INT ivalue );

// Virtual member function
Virtual void dumpme ();
Virtual void foobar ();
PRIVATE:
Double m_fvalue;
};

 

Typedef void (base: * pvvbasememfunc) (void );
Typedef void (derived: * pvvderivememfunc) (void );
Typedef void (base: * pvibasememfunc) (INT );
Typedef void (derived: * pviderivememfunc) (INT );

 

Int _ tmain (INT argc, _ tchar * argv [])
{
Pvibasememfunc = & base: setvalue;
Pviderivememfunc = static_cast <pviderivememfunc> (pvibasemfunc );

Pvvbasememfunc = & base: foobar;
Pvvderivememfunc = static_cast <pvvderivememfunc> (pvvbasemfunc );

Base baseobj;
(Baseobj. * pvibasememfunc) (10 );
(Baseobj. * pvvbasememfunc )();

Derived deriveobj;
(Deriveobj. * pviderivememfunc) (20 );
(Deriveobj. * pvvderivememfunc )();

Return 0;
}

 

_ Deriveobj $=-88
_ Baseobj $=-60
_ Pvvderivememfunc $=-44
_ Pvvbasememfunc $=-32
_ Pviderivememfunc $=-20
_ Pvibasememfunc $=-8
_ Argc $ = 8
_ Argv $ = 12

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.