目录
前言
一、什么是this指针?
this指针长什么样?
简单总结
二、this指针的使用
this指针是否有用?
this指针其他的特点
总结
结语
本教程可能需要一点汇编的底子,不过不懂汇编的也可以放心食用,我都会解释汇编代码的大致意思。使用的工具是vs2010。
在此之前先复习一下一些琐碎的知识吧,在VC编译器中大多数函数调用之后,函数的内部堆栈中:
ebp存放原ebp的值
ebp+4返回地址
ebp+8第一个参数...
ebp-4往上是缓冲区,局部变量一般存放在这里
this指针是“C++封装后的结构体或类中成员函数”的一个默认参数,用于存放当前结构体变量(类的对象)的首地址,它是一个隐藏的参数。
概念听起来很模糊,不急,代码测试之后就能明白。
测试程序demo01.cpp:
#include
#include
struct st_info
{
int a;
int b;
int c;
};
// 结构体的外部定义函数
int ReturnAdd(int a,int b,int c)
{
return a+b+c;
}
int main()
{
struct st_info stinfo = {1,2,3};
int x = ReturnAdd(stinfo.a,stinfo.b,stinfo.c); // 此处下断点,观察反汇编
printf("%d\n",x);
system("pause");
return 0;
}
我们先把函数定义在结构体的外部,现在的结构体就是个简单的结构体,没有进行封装,函数也是个简单的函数,没有this指针。
vs2010 F7编译、F5调试、ALT+8反汇编:
通过反汇编我们观察到,当函数在结构体外部(没有封装)时,函数传递的参数是三个。
我们也说了,函数没有被封装到结构体或类的内部时是没有this指针的
那么下面我们把函数放到结构体内部试试,代码稍微改动:
#include
#include
struct st_info
{
int a;
int b;
int c;
// 结构体的外部定义函数
int ReturnAdd(int a,int b,int c)
{
return a+b+c;
}
};
int main()
{
struct st_info stinfo = {1,2,3};
int x = stinfo.ReturnAdd(stinfo.a,stinfo.b,stinfo.c); // 注意此处也有改动;依旧下断点在这里
printf("%d\n",x);
system("pause");
return 0;
}
首先我们可以发现,我们对函数是没有任何改动的,只是把它封装到结构体里了,对于函数的参数和实现,是没有任何修改的。
那么接下来依旧转到反汇编查看,如下:
红色框的内容就是this指针的样貌,很独特,但又不独特
独特是因为他与其他参数不同,它是用寄存器来传参的
不独特是因为他也就是将【ebp-14】(结构体stinfo对应的首地址)存入了寄存器ecx
那么如果我们不使用this指针,不使用任何参数,不返回任何内容的话,this指针还在吗?
测试如下:
#include
#include
struct st_info
{
int a;
int b;
int c;
// 结构体的外部定义函数
void ReturnAdd() // 删除参数和返回地址
{
}
};
int main()
{
struct st_info stinfo = {1,2,3};
stinfo.ReturnAdd(); // 注意此处也有改动;依旧下断点在这里
system("pause");
return 0;
}
运行一下,转到反汇编:
依旧存在。
1、this指针是编译器默认传入的,通常会使用ecx进行参数的传递。
2、你用或者不用它,他都在。
测试程序demo01.cpp:
#include
#include
struct st_info
{
int a;
int b;
int c;
void initStruct()
{
this->a = 1;
this->b = 2;
this->c = 3;
}
};
int main()
{
struct st_info stinfo;
stinfo.initStruct();
printf("%d %d %d\n",stinfo.a,stinfo.b,stinfo.c);
system("pause");
return 0;
}
像这样,this指针一般都是用与初始化结构体成员,当然还有很多用法,这里就不提了。
但是,可能有人会问,这样好像变得更麻烦了,直接在定义结构体变量的时候初始化不就行了吗?那么this指针的到底有没有用呢?
有的。
当我们定义的结构体成员与函数的形参名字一样的时候,this指针的作用就来了。
测试代码如下:
#include
#include
struct st_info
{
int a;
int b;
int c;
void initStruct(int a, int b, int c)
{
a = a;
b = b;
c = c;
}
};
int main()
{
struct st_info stinfo;
stinfo.initStruct(1,2,3); // 断点
printf("%d %d %d\n",stinfo.a,stinfo.b,stinfo.c);
system("pause");
return 0;
}
如果我们直接写a=a的话,结构体成员真的初始化了吗?
运行如下:
发现结构体变量成员并没有初始化。
下断点、转到反汇编查看:
传入三个值加一个指针
单步步入F11进入函数内部:
可以看到我们直接a = a在底层汇编是没有做任何有意义的事的。
那么大家现在应该能想到,我们如何使用this进行成员初始化,大家可以自己试着做做。
代码如下:
#include
#include
struct st_info
{
int a;
int b;
int c;
void initStruct(int a, int b, int c)
{
this->a = a;
this->b = b;
this->c = c;
}
};
int main()
{
struct st_info stinfo; // 断点
stinfo.initStruct(1,2,3);
printf("%d %d %d\n",stinfo.a,stinfo.b,stinfo.c);
system("pause");
return 0;
}
我们说过,this是指向结构体变量首地址的一个指针,那么this->a肯定指的是这个结构体变量的成员啊。
所以通过this指针,我们就能区别成员变量与参数。
运行如下:
断点、转到反汇编看一下:
三个参数一个指针。
进入函数内部:
vs2010是这样的:
可能不太清晰,掩盖了局部变量的地址。
VC++6是这样的:
分析:
我们知道ecx里存放的是this指针,那么我们先做的是mov dword ptr[ebp-4],ecx
第一行不用看,那个是提升堆栈的时候使用到了。
mov dword ptr[ebp-4],ecx:
这一步将this指针放到了【ebp-4】(局部变量里)
mov eax,dword ptr[ebp-4]:
这一步将this指针取出,放到了eax寄存器里
mov ecx,dword ptr[ebp+8]:
我们上面说了,ebp+8是第一个参数,所以这里将1取出放到了ecx寄存器里
mov dword ptr[eax],ecx:
最后一步将ecx中的1取出,放到了eax(this指针)对应的地址里
总结,上面几步就是将第一个参数1取出放到this指针指向的结构体变量首地址(this->a)中
VC++6代码,继续往下看:
ebp-4:this指针
ebp+c:第二个参数(2)
大家可以自己试着分析下。
总结:
这三步是将第二个参数(2)取出放到this指针指向的结构体变量首地址+4(this->b)的位置上;并且将第三个参数(3)取出放到this指向的结构体变量首地址+8(this->c)的位置上
通过以上测试证明了this指针还是有用的。
1、this指针不能做++--运算,不能重新被赋值。
2、this指针不占用结构体的宽度。
先来验证观点二吧:
#include
#include
struct st_info
{
int x;
int y;
int z;
};
int main()
{
struct st_info stinfo;
printf("%d \n",sizeof(stinfo));
system("pause");
return 0;
}
加上this(函数):
#include
#include
struct st_info
{
int x;
int y;
int z;
void myprint()
{
return ;
}
};
int main()
{
struct st_info stinfo;
stinfo.myprint();
printf("%d \n",sizeof(stinfo));
system("pause");
return 0;
}
运行:
观点2证明成功!
观点1:
无法++;
不要强转,强转也没有什么意义,如果想用指针偏移取值,大可以自己定义一个指针。
那么this是否可以+1,+2呢?
可以,代码如下:
#include
#include
struct st_info
{
int x;
int y;
int z;
void init(int x, int y, int z)
{
this->x = x;
this->y = y;
this->z = z;
}
void myprint()
{
printf("%d \n",this+1);
}
};
int main()
{
struct st_info stinfo;
stinfo.init(1,2,3);
stinfo.myprint();
system("pause");
return 0;
}
编译是没有任何错误的。
那么我们是不是可以通过+1,+2来进行指针偏移取值呢?
不建议!
首先,我们不偏移取一下值:
取到1,没有任何毛病
可能有的人会想,那么偏移4个字节不就能取到2了吗?
取到的是一个垃圾值,为什么呢?
因为我们不知道this这个指针的类型啊,我们知道指针的偏移量除了二级及以上的指针,对于一级指针来说,偏移量是和类型字节大小挂钩的。我们这里根本不知道this指针的类型,怎么可能知道偏移量呢?那又怎么可能正确的取到值呢?
可能有些人想到了百度,搜索this指针类型,然后计算。
还是那句话,没有什么意义,如果想用指针偏移取值,大可以自己定义一个指针。
this指针我们就平常的指向成员就行了,没必要搞些花里胡哨的。
所以观点1:不能++,--是正确的;可以+1,-1但是不建议!
如果真想靠偏移取值,我们大不了自己定义一个,因为这里我们的测试代码中,结构体的成员是int类型,那么我们就定义一个int类型嘛,如下:
#include
#include
struct st_info
{
int x;
int y;
int z;
void init(int x, int y, int z)
{
this->x = x;
this->y = y;
this->z = z;
}
void myprint(int* usr)
{
printf("%d \n",*(usr+1)); // int*+1偏移四个字节
}
};
int main()
{
struct st_info stinfo;
int* pst = (int*)&stinfo; // 定义一个int类型指向结构体
// 不要那么死板,int不一定非要指向int
stinfo.init(1,2,3);
stinfo.myprint(pst); // 传入int指针
system("pause");
return 0;
}
int指针不一定指向int,这里int强转还是有意义的,最起码比强转this有意义。
运行如下:
我们通过自己定义指针进行偏移,成功取出了结构体变量的成员,与直接操作this指针相比,好太多了。
this指针总结 |
---|
1、this指针是编译器默认传入的,不需要手动传入,通常使用ecx进行传递this指针。 |
2、成员函数都有this指针,无论使用与否。 |
3、this指针不能做++ --运算,不能重新被赋值;但是可以做+1,-1运算(不建议)。 |
4、this指针不占用结构体的宽度。 |
如果有错误,请指出;如果有听不懂的地方,可以私信或者评论,我都会进行修改。