码迷,mamicode.com
首页 > 编程语言 > 详细

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

时间:2016-05-07 10:49:56      阅读:174      评论:0      收藏:0      [点我收藏+]

标签:

测试这个功能的初衷是测试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="code" 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++ 如何把this指针传入成员函数 像全局函数一样调用成员函数

标签:

原文地址:http://blog.csdn.net/zzucaicai/article/details/51333161

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!