您的位置:首页 > 编程语言 > C语言/C++

c++ 如何把this指针传入成员函数 像全局函数一样调用成员函数

2016-05-06 17:24 816 查看
测试这个功能的初衷是测试boost里面的bind

boost::bind((&A::sum), &a, _1, _2)

上面的代码是我boost
bind及多线程这篇博客里面的一行代码。我就想boost是怎么做到这样调用一个类的成员函数的。其实成员函数和全局函数无非就是差一个this指针参数。给传进去不就也可以调用了。然而并没有那么简单。看了boost的源码表示太长了。没怎么看懂

然后就自己写代码测试了一下。还用了汇编。。

代码参考  http://www.cppblog.com/woaidongmao/archive/2010/03/11/109444.aspx 

上面讲有两种调用方法,一种是 把this指针放入ecx寄存器,一种是把this指针当作最后一个参数压入栈

我的电脑环境是ubuntu 64位, 从生成的汇编来看,并不是压入ecx,而是压入rdi。 r开头的寄存器都是64位的。然后我就想模拟成员函数的调用方法,手工压入一个this指针,第一次尝试代码如下。运行报segment fault。查看汇编代码发现。在传递参数后edi被用到了。导致之前手工压入的this被破坏了。。。。

#include <cstdio>

using namespace std;

class tt{
public:
/*    void foo(int x) {
printf("arg: x=%d\n", x);
}*/

void foo(int x, char c = 10, char * s = "hello") {
printf("_m_s=%d %d %c %s\n", _m_s, x, c, s);
}
int _m_s;
};

typedef void (tt::* FUNPTR)(int , char, char*);
typedef void (*GLOBALPTR)(int , char, char*);

template< class ToType, class FromType>
void GetMemberFuncAddr(ToType& addr, FromType from) {
union{
FromType _f;
ToType _t;
} ut; //使用union绕过c++的类型检查
ut._f = from;
addr = ut._t;
}

long long This;
int main () {
tt t;
t._m_s =123;
char *ptrc = "hello";
FUNPTR ptr = &tt::foo;
(t.*ptr)(10, 'a', ptrc);
printf("%x\n", ptr);
long long p;
//p = (int)(&tt::foo); 类型不匹配不能强制类型转换
GetMemberFuncAddr(p, &tt::foo);
printf("%x\n", p);

GLOBALPTR p1 = (GLOBALPTR)p;
//    p1(10000, 'c', ptrc);
This = (long long)&t;
__asm__
(
"movq This, %rdi \n"
);
p1(10000, 'c', ptrc);

return 0;
}
g++ -O1 -S test_thisPtr.cpp 生成汇编代码
.file	"test_thisPtr.cpp"
.section	.rodata.str1.1,"aMS",@progbits,1
.LC0:
.string	"_m_s=%d %d %c %s\n"
.section	.text._ZN2tt3fooEicPc,"axG",@progbits,_ZN2tt3fooEicPc,comdat
.align 2
.weak	_ZN2tt3fooEicPc
.type	_ZN2tt3fooEicPc, @function
_ZN2tt3fooEicPc:
.LFB30:
.cfi_startproc
subq	$8, %rsp
.cfi_def_cfa_offset 16
movq	%rcx, %r9
movsbl	%dl, %r8d
movl	%esi, %ecx
movl	(%rdi), %edx
movl	$.LC0, %esi
movl	$1, %edi
movl	$0, %eax
call	__printf_chk
addq	$8, %rsp
.cfi_def_cfa_offset 8
ret
.cfi_endproc
.LFE30:
.size	_ZN2tt3fooEicPc, .-_ZN2tt3fooEicPc
.section	.rodata.str1.1
.LC1:
.string	"hello"
.LC2:
.string	"%x\n"
.text
.globl	main
.type	main, @function
main:
.LFB32:
.cfi_startproc
subq	$24, %rsp
.cfi_def_cfa_offset 32
movl	$123, (%rsp)
//下面5行 显示正常点用成员函数的汇编代码 (t.*ptr)(10, 'a', ptrc)
movl	$.LC1, %ecx
movl	$97, %edx
movl	$10, %esi
movq	%rsp, %rdi
call	_ZN2tt3fooEicPc

movl	$_ZN2tt3fooEicPc, %edx
movl	$0, %ecx
movl	$.LC2, %esi
movl	$1, %edi
movl	$0, %eax
call	__printf_chk
movl	$_ZN2tt3fooEicPc, %edx
movl	$.LC2, %esi
movl	$1, %edi
movl	$0, %eax
call	__printf_chk
movq	%rsp, This(%rip)
#APP
# 49 "test_thisPtr.cpp" 1
movq This, %rdi

# 0 "" 2
#NO_APP
//下面4行显示p1(10000, 'c', ptrc); 调用的代码
//少了 movq %rsp, %rdi命令,即压入this指针
movl	$.LC1, %edx
movl	$99, %esi
movl	$10000, %edi
call	_ZN2tt3fooEicPc
movl	$0, %eax
addq	$24, %rsp
.cfi_def_cfa_offset 8
ret
.cfi_endproc
.LFE32:
.size	main, .-main
.globl	This
.bss
.align 8
.type	This, @object
.size	This, 8
This:
.zero	8
.ident	"GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4"
.section	.note.GNU-stack,"",@progbits
修改代码如下:
把函数的参数都去掉,保证edi不会被用到。然后最重要的一点。把This的类型转成unsigned long long。不然汇编代码中会符号扩展。导致依然报segment fault.
#include <cstdio>

using namespace std;

class tt{
public:
/*    void foo(int x) {
printf("arg: x=%d\n", x);
}*/

void foo() {
printf("_m_s=%d %d\n", _m_s);
}
int _m_s;
};

typedef void (tt::* FUNPTR)();
typedef void (*GLOBALPTR)();

template< class ToType, class FromType>
void GetMemberFuncAddr(ToType& addr, FromType from) {
union{
FromType _f;
ToType _t;
} ut;
ut._f = from;
addr = ut._t;
}

unsigned long long This;
int main () {
tt t;
t._m_s =123;
char *ptrc = "hello";
FUNPTR ptr = &tt::foo;
(t.*ptr)();
printf("%x\n", ptr);
long long p;
//p = (int)(&tt::foo); 类型不匹配不能强制类型转换
GetMemberFuncAddr(p, &tt::foo);
printf("%x\n", p);

GLOBALPTR p1 = (GLOBALPTR)p;
//    p1(10000, 'c', ptrc);
This = (long long)&t;
__asm__
(
"movq This, %rdi \n"
);
p1();

return 0;



汇编代码:

<pre name="c
b095
ode" class="cpp">.file	"test_thisPtr.cpp"
.section	.rodata.str1.1,"aMS",@progbits,1
.LC0:
.string	"_m_s=%d %d\n"
.section	.text._ZN2tt3fooEv,"axG",@progbits,_ZN2tt3fooEv,comdat
.align 2
.weak	_ZN2tt3fooEv
.type	_ZN2tt3fooEv, @function
_ZN2tt3fooEv:
.LFB30:
.cfi_startproc
subq	$8, %rsp
.cfi_def_cfa_offset 16
movl	(%rdi), %edx
movl	$.LC0, %esi
movl	$1, %edi
movl	$0, %eax
call	__printf_chk
addq	$8, %rsp
.cfi_def_cfa_offset 8
ret
.cfi_endproc
.LFE30:
.size	_ZN2tt3fooEv, .-_ZN2tt3fooEv
.section	.rodata.str1.1
.LC1:
.string	"%x\n"
.text
.globl	main
.type	main, @function
main:
.LFB32:
.cfi_startproc
subq	$24, %rsp
.cfi_def_cfa_offset 32
movl	$123, (%rsp)
movq	%rsp, %rdi
call	_ZN2tt3fooEv
movl	$_ZN2tt3fooEv, %edx
movl	$0, %ecx
movl	$.LC1, %esi
movl	$1, %edi
movl	$0, %eax
call	__printf_chk
movl	$_ZN2tt3fooEv, %edx
movl	$.LC1, %esi
movl	$1, %edi
movl	$0, %eax
call	__printf_chk
movq	%rsp, This(%rip)
#APP
# 49 "test_thisPtr.cpp" 1
movq This, %rdi

# 0 "" 2
#NO_APP
call	_ZN2tt3fooEv
movl	$0, %eax
addq	$24, %rsp
.cfi_def_cfa_offset 8
ret
.cfi_endproc
.LFE32:
.size	main, .-main
.globl	This
.bss
.align 8
.type	This, @object
.size	This, 8
This:
.zero	8
.ident	"GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4"
.section	.note.GNU-stack,"",@progbits
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c语言 汇编