bj在它的著作(C++语言的设计与演化 p62)里说在早期的C++里
this是一个可以被赋值的东东,只是在继承在堆栈里很难处理,后来才被淘汰掉
class X{
//...
public:
X();
//...
};
X::X(){
this=my_alloc(sizeof(X));
//...
}
X x;//为x分配内存
既然可以为它赋值那么就意味着this可能是真实存在的这么一个变量
之后我又机会读到了深度探索C++对象模型,书中对this的描述是,this是一个函数参数
float manitude3d(const Point3d *_this){...};
float Point3d::manitude3d()const{...}
这两种方式是等价的,编译器在内部将后者转化为前者,因此
obj.magintude();变成了maginitude_7Point3dFv(&obj);
看完相关的章节,觉得自己对于C++是有了一定的理解,知道this为什么只能
在成员函数中使用,它是一个函数参数,函数参数能在定义它的函数外使用吗?当然不能
也许到这里理解的可能就够了,但是偏偏我这个人有点拧,我就想既然this是真实的变量,那我为什么不能取它地址?给它赋值?又过了一段时间,我开始学习汇编,用的是清华的《汇编语言》作者王爽,这真的是一本不错的书,通俗易懂,拿来入门真是合适不过了,以前我用的是冶金工业出版社的《80X86汇编语言程序设计》讲的我就是晕的一塌糊涂,由此我也想到了,一本好书的定义,应该是能让人一看就茅舍顿开,而一本垃圾书则相反,通过一段时间的学习后(这段时间我待业,刚好有足够的时间)我分别跟踪了VC6.0和BCB6.0中的成员函数执行过程,终于觉得是很久以来想不通的东西都明白了,很爽
先说VC6.0
当程序执行
x.foo();
lea ecx,dword ptr[ebp-4] //对象地址存储到ecx中
call @ILT +10(X::foo)(0040100f) //跳转到0040100f处
0040100f jmp X::foo //0040100f处的jmp跳转的函数真正的地址
void X::foo(){
//...
mov doword ptr[ebp-4],ecx
//将对象地址从ecx中读出,写入到foo的ebp-4处
//...
}
由此可见,在VC中对象地址是通过寄存器变量传递进成员函数,但是进入函数后做完一些初始化的工作以后
把对象地址写入到foo的ebp-4处,这个ebp-4不是调用前的ebp-4,这里存在着压栈等多种操作,很难一下说清楚
由此,了解了VC成员函数的调用过程,我们可以大胆做一些测试程序
void X::foo(X *p){
__asm{
mov eax,doword ptr[ebp+8]
mov dword ptr[ebp-4],eax
//this = p
}
this->a=10;//p->a=10;
}//偷梁换柱,this实际已经指向*p
void X::foo(){
X **p;
__asm{
lea eax,dword ptr[ebp-4]
mov dword ptr [ebp-8],eax
//p=&this
}
(*p)->a=10;//this->a=10;
}//p指向了this因此**p==*this
再说bcb6.0,当程序执行
x.foo();
lea eax,[ebp-0x04]
push eax //将对象地址压栈
call X::foo() //调用X::foo()
在bcb6.0中
#pragma hdrstop
#include <iostream.h>
class A{
public:
virtual void foo(){
this->x=3;
cout<<"this->x= "<<this->x<<endl;
}
virtual void bar(){
this->x=4;
cout<<"this->x= "<<this->x<<endl;
}
int x;
};
void (*pfoo) (A*) ;
int main(){
A a;
int *vptr=(int*)&a; /[v]ptr指向虚指针
int *vtbl=(int*)*vptr; /[v]tbl指向虚表格
pfoo=(void (*)(A*))*vtbl; //指向虚表格第一个单元
(*pfoo)(&a); //引用这个单元
vtbl++; //指向虚表格下一单元
pfoo=(void (*)(A*))*vtbl; //指向虚表格下一单元
(*pfoo)(&a); //引用下一单元
return 0;
}
总结一下:
this指针在概念上可以理解为函数的参数,这个参数是隐藏的
x.foo();这样一个调用的时候被转换成为foo(&x),当然这一切是编译器做的,
对于用户是不可见的
再讲不同编译器的区别
1.VC里foo的原型是 void X::foo(X * const register this)
2.BCB里foo的圆形是 void X::foo(X * const this)
再解释为什么this有一些很神奇的特征
1.为什么没有定义? //编译器定义了
2.为什么不能赋值? //this为const指针
3.为什么不能取地址? //编译器的限制,如果使用汇编,完全可以突破编译器的限制