对C++中的this指针的分析
来源:互联网 发布:淘宝天猫怎么申请 编辑:程序博客网 时间:2024/05/18 01:46
一个示例
首先让我们观察如下代码:
namespace ClassTest { class A { private: int m_int1; int m_int; static int st_int; public: void test1() { cout << "test1" << endl; } void test2() { cout << "test2" << endl; } static void test3() { cout << "test3 " << st_int << endl; } void test4() { m_int = 5; } }; int A::st_int = 5; void test() { A* nullP = NULL; nullP->test1(); nullP->test2(); nullP->test3(); nullP->test4(); }}int main() { ClassTest::test(); system("pause"); return 0;}
你认为这些代码都能成功执行吗?
想必你肯定会奇怪我居然会问这种问题,一个已经指向了NULL
的类指针,怎么可能还能成功调用成员函数呢?
但是假如你对C++的类的实现机制有比较多的了解,就会思考出上述的代码执行情况可能会是这样的:
A* nullP = NULL;nullP->test1();//执行nullP->test2();//执行nullP->test3();//执行nullP->test4();//出错,因为传入的this指针为NULL,但是却想访问非静态成员变量
why?
思考
因为在C++中,类的成员函数的执行并不只是直接跳转到函数体然后就直接进行执行了,而是会在调用成员函数之前,传入一个this
指针(比如上面的代码,传入的this
指针的类型为A* const
,其值为NULL
)。
所以我们可以很容易的想到,当我们使用一个类指针去执行其对应的成员函数的时候,编译器也许会帮我们做下面的事情:
- 根据指针类型找到这个成员函数
- 将
this
放在一个固定寄存器中传入然后在所有参数压栈后再进行压栈 执行成员函数的代码,当使用到非静态成员变量的时候在其前面加上
this->
所以上面的test4函数可能会被编译器添添改改变成下面这种样子:
void test4( A* const this){ this->m_int = 5;};
实践验证,深入剖析
我们可以通过VS生成的汇编代码看看我说的对不对(通过VS的单步调试和反汇编我们可以很容易的做到)
执行以上的代码,我们可以发现在执行test4
函数之前,会先执行如下汇编代码:
0133C5BA mov ecx,dword ptr [nullP] 0133C5BD call ClassTest::A::test4 (013175C2h)
不难看出,在成员函数调用之前,nullP
的值被放在了ecx
寄存器中,然后接着跟踪,test4
内部的汇编代码如下:
void test4(){ 0133BC20 push ebp 0133BC21 mov ebp,esp 0133BC23 sub esp,0CCh 0133BC29 push ebx 0133BC2A push esi 0133BC2B push edi 0133BC2C push ecx 0133BC2D lea edi,[ebp-0CCh] 0133BC33 mov ecx,33h 0133BC38 mov eax,0CCCCCCCCh 0133BC3D rep stos dword ptr es:[edi] 0133BC3F pop ecx 0133BC40 mov dword ptr [this],ecx m_int = 5;0133BC43 mov eax,dword ptr [this] 0133BC46 mov dword ptr [eax+4],5 };
ecx
最后被压栈
注意下面这几行汇编代码:
00F0BC3F pop ecx 00F0BC40 mov dword ptr [this],ecx m_int = 5;0133BC43 mov eax,dword ptr [this] 0133BC46 mov dword ptr [eax+4],5
我们可以看到在访问m_int
的时候,编译器先将 ecx
出栈,然后将ecx
的值放在this
指针应该在的位置(这里我不是太清楚,但是我想的是vs便编译器会将this
指针放在堆栈上的固定位置),然后将this
的值放在eax
寄存器上,然后加上偏移值就可以访问到其成员变量,如果我们将test4
的函数改成如下形式:
void test4(){ m_int1=5;}
然后汇编代码变成了这样:
000CBC40 mov dword ptr [this],ecx m_int1 = 5;000CBC43 mov eax,dword ptr [this] 000CBC46 mov dword ptr [eax],5
我们可以推断,第一个非静态成员变量就放在this
指针指向的位置(在没有析构函数的时候),当我们需要访问其余非静态成员变量时,就加上由其变量类型主导的偏移量。
我们再观察一下上面所有成员函数执行之前的汇编代码:
nullP->test1();000CC5A5 mov ecx,dword ptr [nullP] 000CC5A8 call ClassTest::A::test1 (0A75BDh) nullP->test2();000CC5AD mov ecx,dword ptr [nullP] 000CC5B0 call ClassTest::A::test2 (0A75CCh) nullP->test3();000CC5B5 call ClassTest::A::test3 (0A75C7h) nullP->test4();000CC5BA mov ecx,dword ptr [nullP] 000CC5BD call ClassTest::A::test4 (0A75C2h)
可以发现,我上面说的那些想法都是对的,在执行一个非静态成员函数
之前,this
指针就会被传入,在访问成员变量的时候,this
指针会被使用,所以前三个函数不会出错,因为成员变量没被访问,this
指针就算为NULL
,也不会出错,因为this
指针不会被使用。
我们还可以发现test3
函数执行之前并没有传入this
指针,为什么?
很简单,我就不说了,留给自己思考。
this指针总结
this
指针何时被创建?
在函数调用之前,实际上,成员函数默认第一个参数就为T* const this
,不同的编译器实现方法有所不同。
this
指针何时被销毁?
在函数执行完成之后
this
指针何时不会被当作参数传入?
全局函数,静态函数都不会使用this
指针。
思考以下如下代码:
class B { public: void test()const { } };
这个后置const
的标识符我们肯定经常会使用,但是想必没有过多的深究,我们一般都会把这个当作一个简单的给编译器看的标识符,但是其实这个也可以用const
进行解释:
这个后置const
是用来修饰this
指针的,所以在编译期间,在这个函数作用范围中,对非静态成员的改变都是不被允许的,因为this
指针指向的空间是不能被修改的
- 对C++中的this指针的分析
- C++this指针分析
- C ++的this指针
- C/C++中的对指针的理解
- C++this指针是否可省略分析
- C++this指针的理解
- C++this指针的理解
- C++this指针的用法
- 【c++】this指针的使用
- 【C++】隐含的this指针
- 详解C++中的this指针
- 我对this指针的认识
- 对C++中this指针的认识
- 对 this 指针的理解和作用
- 对c语言中的字符指针变量的解释
- 【C/C++】this指针
- this指针在父子类中的差别-分析OGRE单例模式遇到的问题
- C++中的this 指针的使用
- go 语言 获取物理机器主要参数
- java学习路线,你跑到哪儿了?
- Spring MVC和Struts2的比较
- 把数组排成最小的数
- 给定两个整形变量的值,在不创建临时变量的前提下,交换两个数的内容
- 对C++中的this指针的分析
- 记TP-Link面试被问到的问题
- 两张表用UNION关键字组合,用zTree显示树
- 丑数
- 这条街MV高清视频,熟悉的旋律很好听
- pycharm远程配置
- hdu 3766 Knight's Trip(并不是bfs)
- js 获取系统当前时间的代码
- Direct3D中,DrawIndexedPrimitive的使用方法