在 C++ 中使用 this 指针

在本教程中,我们将首先了解 this 指针的概念。

然后,我们将借助示例演示其用途。最后,我们将看到 this*this 有何不同。

在 C++ 中使用 this 指针

this 指针只是一个隐式可用指针,在非静态类成员函数范围内,它引用或指向调用对象。如果你没有通过定义得到它,那么让我们看一些基础知识来理解这个概念。

我们知道所有非静态数据成员对每个对象都有一个单独的副本。但是,成员函数代码在所有对象之间共享。

同一类的所有对象在需要调用时都从代码段访问相同的函数定义。编译器需要知道调用对象(当前调用成员函数的对象)信息以访问或更新特定于该特定调用对象的正确数据成员。

那么,在函数定义相同的情况下,编译器如何获取调用对象信息呢?

简单的回答是,在程序的编译过程中,编译器会自动在成员函数中添加一个隐藏的隐式指针参数。这个隐式指针被称为 this 指针。

每当通过特定类对象调用成员函数时,调用对象会自动作为隐藏参数提供给 this 指针。现在,编译器可以使用这个指针来访问或修改特定于调用对象的正确数据成员。

注意
类的静态成员不需要 this 指针,因为这些成员(函数和数据成员)在所有对象之间共享。

对静态成员的更新或访问并不特定于任何对象。我们甚至可以在不创建类对象的情况下访问这些成员。

因此,编译器不会对这些成员使用 this 运算符。

让我们看一下代码示例,以了解为什么我们说编译器隐式使用 this 指针以及何时使用它很方便。

#include <iostream>#include <string>using namespace std;
class Person{
private:
    string Name;
public:
    Person(string Name) {
        this->Name = Name; //this->Name is the private member for this object
    }
    void PrintName() {
        cout<<this->Name<<endl;
    }
    void PrintName1() {
        cout<<Name<<endl;
    }
};
int main()
{
    Person P("Alexa");
    P.PrintName();
    P.PrintName1();
    return 0;
}

上面的代码示例定义了一个具有两个不同成员函数的 Person 类来打印私有数据成员 Namemain() 函数中的第一条语句生成一个 Person 对象并将 Alexa 作为参数传递给构造函数。

现在,构造函数中的 this->Name 帮助编译器区分本地参数 Name 和私有数据成员 Name

main() 的后续代码通过 P 调用 printName()printName1()(即 P 成为调用者对象)。

输出:

Alexa
Alexa

两个函数的输出是一样的。这是因为编译器在 PrintName1() 中隐含地在 this-> 前面加上 Name

C++ 中 this*this 的区别

到目前为止,我们已经清楚 this 是指向对象的指针的概念。对于 Person 类型的 objthisPerson*类型。

要记住的另一件事是 this 指针始终是一个 rvalue,不能修改。然而,*this 取消引用 this 指针。

在经历了足够的背景之后,让我们看一个示例代码来了解 this*this 之间的区别。

#include <iostream>using namespace std;
class Counter {
private:
    int Count;
public:
    Counter() { this->Count = 0; }
    void IncreaseCount() { Count++; }
    void PrintCount() { cout << this->Count << endl; }
    Counter* GetCount_Pointer() { return this; }
    Counter GetCount_Copy() { return *this; }
    Counter& GetCount_Reference() { return *this; }
};
int main()
{
    //Section-A
    cout << "Sectio-A" << endl;
    Counter C1;
    C1.IncreaseCount();
    Counter* CounterPtr = C1.GetCount_Pointer(); //CounterObj will be pointing to C1
    CounterPtr->IncreaseCount();
    C1.PrintCount();
    //Section-B
    cout << "Section-B" << endl;
    Counter C2;
    C2 = C1.GetCount_Copy();
    C1.IncreaseCount();
    C1.PrintCount();
    C2.PrintCount();
    //Section-C
    cout << "Section-B" << endl;
    Counter& CounterRef = C1.GetCount_Reference();
    CounterRef.PrintCount();
    return 0;
}

此代码片段创建了一个 Counter 类,其中包含多个 GetCount 方法的方法。第一个方法(即 GetCount_Pointer)返回 this 指针的值,它只是调用对象的地址。

GetCount_Copy 方法返回 *this 而返回类型是 Counter 类型的对象。因此,此函数将返回调用对象的深层副本,这意味着修改返回的副本不会影响原始副本。

最后一个 GetCount_Reference 方法也返回 *this,但此方法的返回类型是一个引用对象(即 Counter&)。编译器不会创建新的深层副本,而是返回对原始对象的别名或引用。

通过别名所做的任何更改也将反映在原始对象中。

在讨论主驱动代码之前,让我们看一下程序的输出。

输出:

Sectio-A
2
Section-B
3
2
Section-B
3

main 方法的 A 部分首先声明 Counter 类型的 C1 对象并通过 IncreaseCount() 递增其 Count。稍后,它用 C1.GetCount_Pointer() 返回的地址初始化一个 CounterPtr 指针。

现在,CounterPtr 将指向 C1。因此,通过 CounterPtr 调用增量函数也会修改 C1

B 部分中的 C2 = C1.GetCount_Copy()C1 的所有当前内容深度复制到 C2,因为 C1.GetCount_Copy()C1 的副本替换。因此,增加 C1 的计数不会影响 C2

C 部分声明了一个 Counter 类型的引用变量,并使用 C1.GetCount_Reference() 返回的任何内容对其进行初始化。由于 C1.GetCount_Reference() 返回 C1 的别名,因此 CounterRef 成为 C1 的另一个名称。

语句 Counter& CounterRef = C1.GetCount_Reference(); 逻辑上等价于 Counter& CounterRef = C1