理解C++中的this指针

在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代码而已~


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值