Когда я должен явно использовать указатель this?

101

Когда я должен явно писать this->memberв методе класса?

dmckee --- котенок экс-модератора
источник
17
Я уверен, что это обман, но это, конечно, не поддается поиску. Не в первый раз хочу, чтобы этот указатель назывался self!
5
Не только это, я бы хотел, чтобы это была ссылка.
rlbond
2
Тем же. : | Кстати, вот почему: research.att.com/~bs/bs_faq2.html#this
GManNickG
11
Очевидно, что этот метод не работает, если человек не знает ответа.
Спросите
3
@JohnH .: Хм, похоже, research.att.com/~bs/сейчас stroustrup.com. Новая ссылка: stroustrup.com/bs_faq2.html#this
GManNickG

Ответы:

121

Обычно это не обязательно this->.

Иногда возникает двусмысленность имени, где его можно использовать для устранения неоднозначности членов класса и локальных переменных. Однако здесь совершенно другой случай, когда this->это явно требуется.

Рассмотрим следующий код:

template<class T>
struct A {
   int i;
};

template<class T>
struct B : A<T> {

    int foo() {
        return this->i;
    }

};

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

Если вы опустите его this->, компилятор не знает, что делать i, поскольку он может существовать или не существовать во всех экземплярах A. Чтобы сказать, что iон действительно является членом A<T>любого T, this->требуется префикс.

Примечание: можно по-прежнему опустить this->префикс, используя:

template<class T>
struct B : A<T> {

    using A<T>::i; // explicitly refer to a variable in the base class

    int foo() {
        return i; // i is now known to exist
    }

};
Просить
источник
8
Хорошее использование декларации using :)
Фейсал Вали
4
Это особенно неприятный случай. Меня это уже кусало.
Джейсон Бейкер,
5
Это может быть глупый вопрос, но я не понимаю, почему iможет не существовать в A. Могу я привести пример?
Кэм Джексон
1
@CamJackson Я пробовал код в Visual Studio. результаты будут одинаковыми независимо от того, существует "this->" или нет. Любая идея?
Peng Zhang
8
@CamJackson: Можно специализировать классы по типу:template<> struct A<float> { float x; };
Macke
31

Если вы объявляете локальную переменную в методе с тем же именем, что и существующий член, вам придется использовать 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

PaV
источник
1
Я бы лучше использовал cout << A :: a << endl; вместо. «это» в данном случае неважно.
siddhant3s
3
Я бы предпочел просто избежать конфликта имен с такими соглашениями, как «m_a» или «a_».
Tom
19

Есть несколько причин, по которым вам может потребоваться thisявное использование указателя.

  • Если вы хотите передать ссылку на свой объект некоторой функции.
  • Когда существует локально объявленный объект с тем же именем, что и объект-член.
  • Когда вы пытаетесь получить доступ к членам зависимых базовых классов .
  • Некоторые люди предпочитают, чтобы нотация визуально устраняла неоднозначность доступа к членам в своем коде.
авакар
источник
7

Хотя обычно мне это не особо нравится, я видел, как другие использовали это-> просто, чтобы получить помощь от intellisense!

Брэд Робинсон
источник
6
  1. Если переменная-член будет скрыта локальной переменной
  2. Если вы просто хотите, чтобы было ясно, что вы вызываете метод / переменную экземпляра


Некоторые стандарты кодирования используют подход (2), поскольку они утверждают, что он упрощает чтение кода.

Пример:
предположим, что MyClass имеет переменную-член с именем count

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

   .....
   count++;
   this->count = count;
}
зебрабокс
источник
6

Есть несколько случаев, когда this необходимо использовать using , а есть другие, где использование 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для местных жителей.

Джон Диблинг
источник
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)но это, кажется, обычное дело.

rlbond
источник
В своем последнем случае, обратите внимание , что вы можете вызвать не- constфункцию члена на временном ( Type(rhs).swap(*this);законно и правильно) , но временно не может связываться с неконстантным опорным параметром (компилятор брака swap(Type(rhs));, а также this->swap(Type(rhs));)
Бен Фойет
4

Вам нужно использовать this-> только в том случае, если у вас есть символ с тем же именем в двух потенциальных пространствах имен. Например:

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 - восстановить Монику
4

Другие варианты использования для этого (как я думал, когда я прочитал резюме и половину вопроса ....), Игнорируя (плохое) неоднозначность имен в других ответах, - это если вы хотите использовать текущий объект, привяжите его к объекту функции или используйте его с указателем на член.

Броски

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 {}

Привязка

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;

ptr-to-member

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.

Macke
источник
3

Вам нужно использовать thisдля устранения неоднозначности между параметрами / локальными переменными и переменными-членами.

class Foo
{
protected:
  int myX;

public:
  Foo(int myX)
  {
    this->myX = myX; 
  }
};
Брайан Р. Бонди
источник
2
Нет, тебе это не нужно , можешь пользоваться . Вы также можете использовать другое имя для аргумента функции, что имеет то преимущество, что у него нет двух сущностей с одинаковым именем.
cmaster - восстановить монику
3

Основное (или, можно сказать, единственное) назначение thisуказателя состоит в том, что он указывает на объект, используемый для вызова функции-члена.

Исходя из этого, мы можем иметь некоторые случаи, когда только использование thisуказателя может решить проблему.

Например, мы должны вернуть вызывающий объект в функции-члене с аргументом того же объекта класса:

class human {

... 

human & human::compare(human & h){
    if (condition)
        return h;       // argument object
    else 
        return *this;   // invoking object
    }
};
Тревор
источник
2

Еще один интересный случай явного использования указателя this я нашел в книге «Эффективный C ++».

Например, скажем, у вас есть константная функция вроде

  unsigned String::length() const

Вы не хотите рассчитывать длину строки для каждого вызова, поэтому вы хотите кэшировать ее, выполняя что-то вроде

  unsigned String::length() const
  {
    if(!lengthInitialized)
    {
      length = strlen(data);
      lengthInitialized = 1;
    }
  }

Но это не скомпилируется - вы меняете объект в константной функции.

Чтобы решить эту проблему, нужно преобразовать this в неконстантный this :

  String* const nonConstThis = (String* const) this;

Затем вы сможете сделать это

  nonConstThis->lengthInitialized = 1;
Ариэль
источник
3
Или вы можете сделать lengthизменяемым или даже поместить его во вложенную структуру. Отказ от константности почти никогда не бывает хорошей идеей.
Ричард Дж. Росс III
3
Пожалуйста, не надо. Если член должен быть изменен из constфункций-членов, он должен быть mutable. В противном случае вы усложняете жизнь себе и другим сопровождающим.
Дэвид Родригес - dribeas