在C++中,this指针隐式存在于实例化对象当中。而对对象的取址操作和指针赋值实际上赋值的正是对象的this指针,如:
class A;
class B : public A;
……
B b;
A* pa = &b; // 对象取址赋值给指针
在上面的代码中,b对象是一个子类对象,所谓的取址&也即是将对象b的this指针的值赋值给一个它的父类指针。当然,本质上来说,对象b的this指针其实就是等于&b,两者没有本质区别。这行看似简单的代码对于我们理解对象的调用将会有很好的帮助,原因自然涉及到C++具有的一个重要特性:多态。
关于多态的用法和好处不是本文要阐述的内容,凡是涉及到C++面向对象程序设计的项目一般多少要用到多态(不然用纯C写与C++也就没有太多的区别)。那么这个this指针到底有什么用呢?可以简单看一下以下两段代码,比较差别:
#include <iostream>
using namespace std;
class A {
public:
A(){cout<<"A:"<<this<<endl;}
virtual void fun() {
cout << "A's fun()" << endl;
cout<<this<<endl;
show(); // 等价于this.show();
A::show(); // 强制执行父类自身的show方法
}
virtual void show() {
cout << "A's show()" << endl;
cout<<this<<endl;
}
};
class B: public A {
public:
B(){cout<<"B:"<<this<<endl;}
virtual void fun() {
cout << "B's fun()" << endl;
cout<<this<<endl;
A::fun();
}
virtual void show() {
cout << "B's show()" << endl;
cout<<this<<endl;
}
};
int main() {
B bobj;
A *aptr = &bobj; // 将子类bobj的this指针赋值给aptr指针。因此在执行时,所有的方法默认都是调用这个子类的this指针!
aptr->fun();
system("pause");
}
程序的执行结果是:
上面的程序用到了简单的多态,只需要注意一点即可:当类存在继承关系时,this指针指向的永远是真正实例化的对象地址。这话不太准确也不好理解,从代码的执行看就非常清晰了:当程序在执行
aptr->fun();
时,系统在子类的show方法中调用了父类的fun方法,并在父类内部执行了一个show方法。那么这个show方法执行的为什么不是父类A中的show方法却调用了对父类A来说似乎不可见的子类B中的show方法了呢(这话很拗口T T)?原因就在这个this指针!这里为了方便大家调试加了很多的输出语句用于查看对应的this指针值:正是因为在执行多态的过程中,真正的实例化对象是子类对象,通过this指针访问成员函数时,由于虚函数的存在所以默认的show()方法等价于this.show(),而我们看到这个this其实是子类的地址,那么她调用的也就正是子类的show()方法了!那么如果要强制访问父类本身的show方法,就需要改为A::show()。
到了这里或许已经可以基本理解this指针在多态中的作用。不妨再花点时间看一下下面这个程序,大体结构和前面的程序是一样的,如果觉得程序有点绕可以将那些输出this指针的语句删除:
#include <iostream>
using namespace std;
class Base {
public:
Base():i(1) {
cout<<i<<endl;
cout<<this<<endl;
f(); // 等价于this.f();但有差异
}
virtual void f() {
cout<<i<<endl;
cout<<this<<endl;
i *= 50;
}
virtual void print() {
cout <<i << endl;
cout<<this<<endl;
f();
}
private:
int i;
};
class Derived : public Base {
public:
Derived():i(2) {
cout<<i<<endl;
cout<<this<<endl;
f();
}
virtual void f() {
cout<<i<<endl;
cout<<this<<endl;
i*=30;
}
private:
int i;
};
void main()
{
Derived d;
Base *p = &d;
p->print();
system("pause");
}
程序输出:
发现this指针确实无论在对象的哪个函数里执行都是一样的。但亲们注意到没,在实例化这个子类对象过程中,虚函数f()被多次调用,其中包括在父类的构造函数中被调用。如果单步调试会发现,在父类Base的构造中执行的f()依然是父类自身的f()函数。而按照前面的逻辑,在实例化对象中,this指针是隐式存在的,程序的输出也验证了这一点。那么既然如此f()这个虚函数在子类中又重新实现了(多态),在父类的构造函数中执行f()相当于执行this.f(),这个this其实是子类的对象指针,为什么会去执行父类中原本的f()函数呢?这个又涉及到C++面向对象设计中类的初始化顺序问题!
1 构造父类成员对象
2 构造子类成员对象
3 调用父类构造函数
4 调用子类构造函数
这四项工作的时间顺序是怎样的呢?
原则是:先成员后构造函数,先父类构造后子类构造 == 先父类成员,父类构造;再子类成员,子类构造
所以,在这里父类构造完成前,子类对象还是不完整的!这话不太准确,但试想一下,如果编译器允许在父类构造的时候直接调用子类重写的函数f(),这是多么危险的行为:万一你在子类重写的f()中用到了子类新的数据成员,而根据前面的规则“ 先父类成员,父类构造;再子类成员,子类构造”,这时候子类的成员都还没实例化出来!所以,可以这么理解this:如果对象已经实例化完成,那么this指向的就是你new出来的对象;如果是构造过程中,那么this就是指当前对象。说到底,this就是一个内存地址而已,对象的实例化和函数调用不过是在这个地址基础上的一段01代码而已~