C++ this指针(直戳本质)

为了能让大家看清 this 指针的本质,我们会先讲一点 C++ 的历史——C++ 程序到C程序的翻译过程。

C++ 程序到C程序的翻译

C++ 是在C语言的基础上发展而来的,第一个 C++ 的编译器实际上是将 C++ 程序翻译成C语言程序,然后再用C语言编译器进行编译。

C语言没有类的概念,只有结构,函数都是全局函数,没有成员函数。翻译时,将 class 翻译成 struct、对象翻译成结构变量是显而易见的,但是对类的成员函数应该如何翻译?对myCar.Modify();这样通过一个对象调用成员函数的语句,又该如何翻译呢?

C语言中只有全局函数,因此成员函数只能被翻译成全局函数;myCar.Modify();这样的语句也只能被翻译成普通的调用全局函数的语句。那如何让翻译后的 Modify 全局函数还能作用在 myCar 这个结构变量上呢?答案就是引入“this 指针”。下面来看一段 C++ 程序到C 程序的翻译。

C++程序:
class CCar
{
public:
    int price;
    void SetPrice(int p);
};

void CCar::SetPrice(int p)
{
    price=  p;
}
int main()
{
    CCar car;
    car.SetPrice(20000);
    return 0;
}
翻译后的C程序(此程序应保存为扩展名为 .c 的文件后再编译):
struct CCar
{
    int price;
};
void SetPrice(struct CCar* this, int p)
{
    this->price = p;
}
int main()
{
    struct CCar car;
    SetPrice(&car, 20000);
    return 0;
}
可以看出,类被翻译成结构体,对象被翻译成结构变量,成员函数被翻译成全局函数。但是C程序的全局函数 SetPrice 比 C++ 的成员函数 SelPrice 多了一个参数,就是struct CCar *thiscar.SetPrice(20000);被翻译成SetPrice(&car, 20000);,后者在执行时,this 形参指向的正是 car 这个变量,因而达到了 SetPrice 函数作用在 car 变量上的效果。

思考题:以上翻译还不完整,因为构造函数的作用没有体现出来。思考构造函数应该如何翻译。另外,静态成员函数和静态成员变量应如何翻译?

this 指针的作用

实际上,现在的C编译器从本质上来说也是按上面的方法来处理成员函数和对成员函数的调用的,即非静态成员函数实际上的形参个数比程序员写的多一个。多出来的参数就是所谓的“this指针”。这个“this指针”指向了成员函数作用的对象,在成员函数执行的过程中,正是通过“Ihis指针”才能找到对象所在的地址,因而也就能找到对象的所有非静态成员变量的地址。

下面程序的运行结果能够证明这一点:
#include <iostream>
using namespace std;
class A
{
    int i;
public:
    void Hello(){ cout << "hello" << endl; }
};
int main()
{
    A* p = NULL;
    p -> Hello();
}
程序的输出结果是:
hello

在上面的程序中,p 明明是一个空指针,为何通过它还能正确调用 A 的成员函数 Hello 呢?因为,参考上面 C++ 到C程序的翻译,P->Hello()实质上应该是Hello(p),在翻译后的 Hello 函数中,cout 语句没有用到 this 指针,因此依然可以输出结果。如果 Hello 函数中有对成员变量的访问,则程序就会出错。

C++ 规定,在非静态成员函数内部可以直接使用 this 关键字,this 就代表指向该函数所作用的对象的指针。看下面的例子:
#include <iostream>
using namespace std;
class Complex {
public:
    double real, imag;
    Complex(double r, double i) : real(r), imag(i) {}
    Complex AddOne()
    {
        this->real++;
        return *this;
    }
};
int main()
{
    Complex cl(1, 1), c2(0, 0);
    c2 = cl.AddOne();
    cout << c2.real << "," << c2.imag << endl; //输出 2,1
    return 0;
}
第 9 行,this 指针的类型是 Complex*。因为 this 指针就指向函数所作用的对象,所以 this->rear 和 real 是完全等价的。*this代表函数所作用的对象,因此执行第 16 行,进入 AddOne 函数后,*this实际上就是 c1。因此的 c2 值会变得和 c1 相同。

因为静态成员函数并不作用于某个对象,所以在其内部不能使用 this 指针;否则,这个 this 指针该指向哪个对象呢?