C++拾遗--this指针
前言
在用C++进行面向对象编程时,this指针是一个人尽皆知的东西。但我们真的清楚它吗?下面我们对它的存在意义和使用方法一一进行探讨。
this指针
存在意义:我们为何需要this指针?
看一个简单的例子
#include <iostream> using namespace std; class MyClass { protected: int a; public: MyClass(int a) :a(a) {} void setA(int a) { this->a = a; } int getA() const { return a; } }; int main() { MyClass my1(0); MyClass my2(1); my1.setA(2); my2.setA(3); cout << "my1::a = " << my1.getA() << endl; cout << "my2::a = " << my2.getA() << endl; cin.get(); return 0; }
运行
这个例子实在是太简单了!大家用脚指头都能看明白。我们需要深入思考下:
1.setA方法中明明没有this形参,为何在方法体中可以使用this?
答:this形参是隐式定义的,它代表当前对象的内存地址。我们不写,并不代表它不存在。
2.在设计setA方法时,我故意把方法形参(int a)和类成员变量(int MyClass::a)设计成同名的。以至于function body中必须写成 this->a=a;才能完成赋值。若把形参写成不同名的,比如int A,是否可以省略this?
答:可以的。但本质上还是this->a=A;还是那句:我们不写,并不代表它不存在。编译器在进行编译时,会自动把“a=A”改为“this->a=A;”。
3.从以上两个问题还没弄清this的存在意义吗?那就多说一句,程序在内存中的存储原理:代码区是公共的,静态变量是公共的。公共是只有一份的意思。
前者是公共的理由是:节约内存。后者是公共的理由是:逻辑上本该如此!(静态变量的存在意义)
总之,类MyClass的任何对象在调用setA方法时,都是调用同一份代码。但为何会有不同的效果呢?对象my1在调用setA时,是对my1.a进行赋值。对象my2在调用setA时,是对my2.a赋值。它们执行的代码相同,却不会乱掉,就是因为this指针的存在!
这就完了吗?还没有,我们需要进一步指出:this指针隐式定义于方法形参中。那么它的位置是?这不好说,我的推测:就是第一个形参。我的理由:方法调用时,传入的实参是从右向左入栈的,出栈时,第一个位置的参数当然就最先出栈。(若有不同的看法,欢迎讨论!)以下的讨论就暂且基于这种假设。
所以,setA(int a);本质上应该是 setA(MyClass * const this, int a);
my1.setA(2);本质上应该是 my1.setA(&my1, 2);
再深入,细心的你会发现,我写的是 MyClass * const this,而不是 MyClass * this,多了一个const,为何?这当然也是有理由的!
理由很简单:若this不是const的,则可以随意更改this的指向,如下代码:
void setA(int a) { this = &my; this->a = a; }
当然,这样的代码肯定是通不过编译的。每次对setA的调用都变成了对my.a的赋值。
总结:默认情况下,this的类型是指向类类型非常量版本的常量指针。(《C++Primer》)
还没完,我们还得继续深入……
const成员函数是怎么一回事?它和this有联系吗?
我们知道,const成员函数是不可以对成员变量进行修改的。那它是如何做到的呢?
原因:const对成员函数的修饰,就是对this的修饰。
上面的例子中的int getA()const;本质上是 int getA(MyClass const * const this);
至此,真相终于大白。