何时应该显式使用`this`指针?

125

在一个类的方法中,什么情况下应该显式地写出 this->member


21
我相信这是一个重复的问题,但它当然是无法搜索到的。不是第一次了,我希望这个指针被称为self! - anon
6
不仅如此,我希望它成为一个参考资料。 - rlbond
2
同样。:| 顺便说一下,这是为什么:http://www.research.att.com/~bs/bs_faq2.html#this - GManNickG
11
如果这个人不知道答案,那么这种方法显然是行不通的。 - ASk
4
ه—¯ï¼Œçœ‹èµ·و‌¥research.att.com/~bs/çژ°هœ¨هڈکوˆگن؛†stroustrup.comم€‚و–°é“¾وژ¥وک¯ï¼ڑhttp://www.stroustrup.com/bs_faq2.html#thisم€‚ - GManNickG
显示剩余2条评论
12个回答

147
通常情况下,你不需要使用this->,因为它是隐含的。
有时候,存在名称歧义,可以用它来消除类成员和局部变量之间的歧义。然而,在以下完全不同的情况中,需要明确使用this->
考虑以下代码:
template<class T>
struct A {
   T i;
};

template<class T>
struct B : A<T> {
    T foo() {
        return this->i; //standard accepted by all compilers 
        //return i; //clang and gcc will fail
        //clang 13.1.6: use of undeclared identifier 'i'
        //gcc 11.3.0: 'i' was not declared in this scope
        //Microsoft C++ Compiler 2019 will accept it
    }

};

int main() {
    B<int> b;
    b.foo();
}

如果省略 this->,一些编译器不知道如何处理 i。为了告诉编译器 i 确实是 A<T> 的成员,对于任何 T,需要使用 this-> 前缀。
注意:仍然可以通过使用以下方式省略 this-> 前缀:
template<class T>
struct B : A<T> {
    int foo() {
        return A<T>::i; // explicitly refer to a variable in the base class 
        //where 'i' is now known to exist
    }

};

10
这可能是一个愚蠢的问题,但我不明白为什么iA中可能不存在。我可以举个例子吗? - Cam Jackson
1
@CamJackson 我在Visual Studio上尝试了这段代码,无论是否存在"this->",结果都是相同的。你有什么想法吗? - Peng Zhang
14
可以根据类型来专门化类: template<> struct A<float> { float x; }; - Macke
注意:仍然可以使用“using”省略“this->”前缀,但需要小心虚函数。 - L. F.
5
Visual Studio编译器曾经忽略在这些情况下需要使用“this”的要求,这不符合C++标准,请参见Microsoft Docs获取详细信息。 - Kyrion

35

如果你在一个方法中声明了一个和现有成员同名的局部变量,那么你将需要使用 this->var 来访问类成员,而不是局部变量。

#include <iostream>
using namespace std;
class A
{
    public:
        int a;

        void f() {
            a = 4;
            int a = 5;
            cout << a << endl;
            cout << this->a << endl;
        }
};

int main()
{
    A a;
    a.f();
}

输出:

5
4


1
我最好使用cout << A::a << endl; 而不是``this"在这种情况下不重要。 - siddhant3s
5
我更倾向于避免与“m_a”或“a_”等惯例发生名称冲突。 - Tom

24

有几个原因可能需要显式使用 this 指针。

  • 当要将对象的引用传递给某个函数时。
  • 当有一个与成员对象同名的局部声明对象时。
  • 当您试图访问依赖基类的成员时。
  • 一些人喜欢使用这种记法以在代码中直观地消除成员访问的歧义。

9

虽然我通常不太喜欢,但我看到其他人使用这个->只是为了从智能感知中获得帮助!


为什么你不喜欢它呢?我觉得这样做可以减轻思考“这是局部变量还是数据成员”的认知负担。 - User 10482

8

有些情况下必须使用this,而在其他情况下,使用this指针是解决问题的一种方式。

1)可用替代方案:解决本地变量和类成员之间的歧义,如@ASk所示

2)无替代方案:从成员函数返回this指针或引用。当重载operator+operator-operator=等时,这经常被执行(也应该这样做):

class Foo
{
  Foo& operator=(const Foo& rhs)
  {
    return * this;
  }
};

这样做可以使用一种称为“方法链式调用”的惯用语,其中您可以在一行代码中对一个对象执行多个操作,例如:
Student st;
st.SetAge (21).SetGender (male).SetClass ("C++ 101");

有些人认为这是简洁的,而另一些人则认为这是可憎的。我属于后者。

3) 无替代方案: 用于解决依赖类型中的名称。当使用模板时,例如以下示例:

#include <iostream>


template <typename Val>
class ValHolder
{
private:
  Val mVal;
public:
  ValHolder (const Val& val)
  :
    mVal (val)
  {
  }
  Val& GetVal() { return mVal; }
};

template <typename Val>
class ValProcessor
:
  public ValHolder <Val>
{
public:
  ValProcessor (const Val& val)
  :
    ValHolder <Val> (val)
  {
  }

  Val ComputeValue()
  {
//    int ret = 2 * GetVal();  // ERROR:  No member 'GetVal'
    int ret = 4 * this->GetVal();  // OK -- this tells compiler to examine dependant type (ValHolder)
    return ret;
  }
};

int main()
{
  ValProcessor <int> proc (42);
  const int val = proc.ComputeValue();
  std::cout << val << "\n";
}

4) 可供选择的方案: 作为编码风格的一部分,用于记录哪些变量是成员变量而非局部变量。我更喜欢一种不同的命名方案,其中成员变量永远不能与局部变量具有相同的名称。目前,我使用mName表示成员变量,name表示局部变量。


对于第三点,当你说“没有替代方案”时,实际上还有其他几种方法:1)int ret = 6 * VahHolder<Val>::GetVal(); 或者2)在类(而不是函数)中使用 using ValHolder<Val>::GetVal;,这也使得 GetVal 在依赖上下文中可以进行未限定查找。https://godbolt.org/z/n5PY3j51c - Chris Uzdavinis

6
  1. 当成员变量被局部变量隐藏时
  2. 如果你只想明确地表明你正在调用一个实例方法/变量


一些编码标准使用第二种方法,因为他们声称它可以使代码更易读。

例如:
假设MyClass有一个名为'count'的成员变量

void MyClass::DoSomeStuff(void)
{
   int count = 0;

   .....
   count++;
   this->count = count;
}

5

除了其他答案中提到的(不好的)命名消歧之外,我在阅读摘要和问题一半时想到的另一个用途是:如果您想转换当前对象,将其绑定到函数对象或使用指向成员的指针。

类型转换

void Foo::bar() {
    misc_nonconst_stuff();
    const Foo* const_this = this;
    const_this->bar(); // calls const version

    dynamic_cast<Bar*>(this)->bar(); // calls specific virtual function in case of multi-inheritance
} 

void Foo::bar() const {}

Binding

void Foo::baz() {
     for_each(m_stuff.begin(), m_stuff.end(),  bind(&Foo:framboozle, this, _1));        
     for_each(m_stuff.begin(), m_stuff.end(), [this](StuffUnit& s) { framboozle(s); });         
} 

void Foo::framboozle(StuffUnit& su) {}

std::vector<StuffUnit> m_stuff;

指向成员的指针

void Foo::boz() {
    bez(&Foo::bar);
    bez(&Foo::baz);
} 

void Foo::bez(void (Foo::*func_ptr)()) {
    for (int i=0; i<3; ++i) {
        (this->*func_ptr)();
    }
}

希望这能有所帮助,展示出这个不仅仅局限于使用“this->member”的其他用途。

5

有另一种情况是调用操作符。例如,不要写成

bool Type::operator!=(const Type& rhs)
{
    return !operator==(rhs);
}

你可以说

bool Type::operator!=(const Type& rhs)
{
    return !(*this == rhs);
}

这可能更易读。另一个例子是复制和交换:

Type& Type::operator=(const Type& rhs)
{
    Type temp(rhs);
    temp.swap(*this);
}

我不知道为什么没有写成 swap(temp),但这似乎很常见。


在你的最后一个情况中,请注意,你可以在临时对象上调用非const成员函数(Type(rhs).swap(*this);是合法和正确的),但是临时对象不能绑定到非const引用参数(编译器会拒绝swap(Type(rhs));以及this->swap(Type(rhs));)。 - Ben Voigt

4

如果你在两个潜在的命名空间中有相同名称的符号,那么你只需要使用这个->。例如:

class A {
public:
   void setMyVar(int);
   void doStuff();

private:
   int myVar;
}

void A::setMyVar(int myVar)
{
  this->myVar = myVar;  // <- Interesting point in the code
}

void A::doStuff()
{
  int myVar = ::calculateSomething();
  this->myVar = myVar; // <- Interesting point in the code
}

在代码的有趣点处,引用myVar将会指向本地(参数或变量)myVar。为了访问也称为myVar的类成员,需要显式使用“this->”。


这是使用 this-> 的一个微不足道的用法(只需为本地变量命名即可避免)。所有真正有趣的 this 用法都没有在这个答案中提到。 - cmaster - reinstate monica

3

你需要使用this来区分参数/局部变量和成员变量。

class Foo
{
protected:
  int myX;

public:
  Foo(int myX)
  {
    this->myX = myX; 
  }
};

2
不,你不需要它,你可以使用它。你也可以为函数参数使用不同的名称,这样有一个好处,就是不会有两个具有相同名称的实体。 - cmaster - reinstate monica

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接