理解this指针

前言

从前也对this指针感到疑惑,比如,它为什么能够用来表示当前对象。

后来了解了一些底层原理后,就逐渐想通了。

定义

从语法上来讲,this指针可以看做是当前对象,我们可以用它来间接访问对象里的成员和函数。

从本质上来说,this指针是一个特殊类型的指针,它保存了对象的地址。

如果你不懂什么是指针的话,可以看一下博主写的这篇文章 理解指针

原理

为了方便说明,使用如下代码来解释this指针的原理。

#include<stdio.h>

class TestThis
{

public:	
	TestThis()
	{
	}
};
int main()
{
	TestThis _this;
	return 0;
}

在Windows XP 上用VC6.0 编译后,打开IDA。如下所示。

.text:0040D3F0 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:0040D3F0 _main           proc near               ; CODE XREF: _main_0j
.text:0040D3F0
.text:0040D3F0 var_44          = byte ptr -44h
.text:0040D3F0 var_4           = byte ptr -4
.text:0040D3F0 argc            = dword ptr  8
.text:0040D3F0 argv            = dword ptr  0Ch
.text:0040D3F0 envp            = dword ptr  10h
.text:0040D3F0
.text:0040D3F0                 push    ebp
.text:0040D3F1                 mov     ebp, esp
.text:0040D3F3                 sub     esp, 44h
.text:0040D3F6                 push    ebx
.text:0040D3F7                 push    esi
.text:0040D3F8                 push    edi
.text:0040D3F9                 lea     edi, [ebp+var_44]
.text:0040D3FC                 mov     ecx, 11h
.text:0040D401                 mov     eax, 0CCCCCCCCh
.text:0040D406                 rep stosd
.text:0040D408                 lea     ecx, [ebp+var_4] ; this
.text:0040D40B                 call    j_??0TestThis@@QAE@XZ ; TestThis::TestThis(void)
.text:0040D410                 xor     eax, eax
.text:0040D412                 pop     edi
.text:0040D413                 pop     esi
.text:0040D414                 pop     ebx
.text:0040D415                 add     esp, 44h
.text:0040D418                 cmp     ebp, esp
.text:0040D41A                 call    __chkesp
.text:0040D41F                 mov     esp, ebp
.text:0040D421                 pop     ebp
.text:0040D422                 retn
.text:0040D422 _main           endp
.text:0040D422
.text:0040D422 ; -----------------------------------------------

观察下面这两行

.text:0040D408                 lea     ecx, [ebp+var_4] ; this
.text:0040D40B                 call    j_??0TestThis@@QAE@XZ ; TestThis::TestThis(void)

可以看到编译器把 ebp-4 (即对象的首地址)送入 ecx 中,以此作为 参数 来调用构造函数。
而 this指针保存的是对象的首地址。
由此可知,ecx 被当成this指针 用来初始化构造函数。

继续查看构造函数,

.text:0040D430 ; public: __thiscall TestThis::TestThis(void)
.text:0040D430 ??0TestThis@@QAE@XZ proc near           ; CODE XREF: TestThis::TestThis(void)j
.text:0040D430
.text:0040D430 var_44          = byte ptr -44h
.text:0040D430 var_4           = dword ptr -4
.text:0040D430
.text:0040D430                 push    ebp
.text:0040D431                 mov     ebp, esp
.text:0040D433                 sub     esp, 44h
.text:0040D436                 push    ebx
.text:0040D437                 push    esi
.text:0040D438                 push    edi
.text:0040D439                 push    ecx
.text:0040D43A                 lea     edi, [ebp+var_44]
.text:0040D43D                 mov     ecx, 11h
.text:0040D442                 mov     eax, 0CCCCCCCCh
.text:0040D447                 rep stosd
.text:0040D449                 pop     ecx
.text:0040D44A                 mov     [ebp+var_4], ecx
.text:0040D44D                 mov     eax, [ebp+var_4]
.text:0040D450                 pop     edi
.text:0040D451                 pop     esi
.text:0040D452                 pop     ebx
.text:0040D453                 mov     esp, ebp
.text:0040D455                 pop     ebp
.text:0040D456                 retn
.text:0040D456 ??0TestThis@@QAE@XZ endp
.text:0040D456

首先push ecx是为了防止寄存器冲突,因为初始化堆栈也要用到ecx。

虽然在这里this指针并没有做什么事情,但是你可以看到this被保存到 ebp-4 (局部变量)中,并且作为函数的第一个参数。

保存this指针是为了以后能对成员和函数作访问,因为this指针保存了对象首地址,所以只要加上偏移量就能得到对象里的成员位置。

使用ecx寄存器来传递参数这种方式被称为 thiscall,是WIndows平台特有的传参方式。

理解

既然this指针是一个特殊类型的指针,那么它也符合指针的特性,可以使用指针的操作

我们是否可以用this指针来打印构造函数的返回地址呢?

当然是可以的。

看如下代码。

#include<stdio.h>

class TestThis
{

public:	
	TestThis()
	{
	printf("The return address of TestThis is: 0x%p \n",(int *)(this+1)+1);

	}

	double a;	
	float b; 
	int c;
	char d;
};
int main()
{
	TestThis _this;
	printf("The size of _this is: 0x%x\n",sizeof(_this));
	getchar();
	return 0;
}

运行结果如图。

在这里插入图片描述

部分堆栈中数据如下

0012FF68  |CCCCCCCC // a 占8个字节,也是this指针指向的地址
0012FF6C  |CCCCCCCC 
0012FF70  |CCCCCCCC // b
0012FF74  |CCCCCCCC // c
0012FF78  |CCCCCCCC // d
0012FF7C  |CCCCCCCC //字节对齐
0012FF80  ]0012FFC0 // 上个函数的ebp
0012FF84  |00401609  返回到 test.<模块入口点>+0E9 来自 test.00401005 // 返回地址

this指针的大小是对象的大小即24个字节,this+1 可以直接把this指针移动到尾部。

因为保存的地址大小是4个字节,所以this可以转成int指针,再加一得到返回地址。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值