Коли я повинен явно використовувати вказівник `this`?


97

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


16
Я впевнений, що це обман, але, звичайно, це неможливо дослідити. Не вперше, я б хотів, щоб цей вказівник називався self!

5
Мало того, я хотів би, щоб це було довідкою.
rlbond

2
Те саме. : | Ось чому, до речі: research.att.com/~bs/bs_faq2.html#this
GManNickG

11
Цей метод очевидно не працює, якщо людина не знає відповіді.
ASK

3
@JohnH .: Гм, схоже research.att.com/~bs/, зараз stroustrup.com. Нове посилання: stroustrup.com/bs_faq2.html#this
GManNickG

Відповіді:


118

Зазвичай, цього не потрібно, 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
Приємне використання декларації :)
Фейсал Валі

3
Це особливо неприємний випадок. Мене це кусало раніше.
Джейсон Бейкер

5
Це може бути безглузде питання, але я не розумію, чому в Росії iможе не існувати A. Чи можу я отримати приклад?
Cam Jackson

1
@CamJackson Я спробував код на візуальній студії. результати однакові, незалежно від того, існувало це чи ні. Будь-яка ідея?
Пен Чжан

8
@CamJackson: Можна спеціалізуватись на заняттях за типом:template<> struct A<float> { float x; };
Macke

31

Якщо ви оголосите локальну змінну в методі з тим самим іменем, що і існуючий член, вам доведеться використовувати цей-> 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; замість цього. `` це '' у цьому випадку неважливо.
siddhant3s

3
Я вважаю за краще просто уникати зіткнення імені з умовами, такими як "m_a" або "a_".
Том

19

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

  • Коли ви хочете передати посилання на свій об’єкт якійсь функції.
  • Коли існує локально оголошений об'єкт з тим самим іменем, що і об'єкт-член.
  • Коли ви намагаєтеся отримати доступ до членів залежних базових класів .
  • Деякі люди вважають за краще, щоб позначення візуально розмежували доступ членів у своєму коді.

7

Хоча зазвичай мені це не подобається, я бачив, як інші використовують це-> просто, щоб отримати допомогу від інтелігенції!


6
  1. Там, де змінна члена буде прихована локальною змінною
  2. Якщо ви просто хочете чітко зрозуміти, що ви викликаєте метод / змінну екземпляра


Деякі стандарти кодування використовують підхід (2), оскільки вони стверджують, що це полегшує читання коду.

Приклад:
Припустимо, що MyClass має змінну члена під назвою 'count'

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

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

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);законно і правильно) , але тимчасово не може зв'язуватися з неконстантним опорним параметром (компілятор шлюбу swap(Type(rhs));, а також this->swap(Type(rhs));)
Бен Фойет

5

Мало випадків, коли 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для місцевих жителів.


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 - відновити моніку

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)();
    }
}

Сподіваюся, це допомагає показати інші способи використання цього, а не лише цього-> члена.


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

Я знайшов ще один цікавий випадок явного використання вказівника "цей" в книзі "Ефективний C ++".

Наприклад, скажімо, у вас функція const, як

  unsigned String::length() const

Ви не хочете обчислювати довжину рядка для кожного дзвінка, отже, ви хочете кешувати його, роблячи щось подібне

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

Але це не складеться - ви змінюєте об'єкт у функції const.

Трюк, щоб вирішити це, вимагає відкинути це до неконструктивного цього :

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

Тоді ви зможете зробити вище

  nonConstThis->lengthInitialized = 1;

3
Або ви можете зробити lengthзмінний або навіть помістити його в вкладені структури. Відганяти незграбність майже ніколи не є хорошою ідеєю.
Річард Дж. Росс III

3
Будь ласка, не варто. Якщо член повинен бути змінений з constфункцій члена, він повинен бути mutable. Інакше ви ускладнюєте життя іншим обслуговуючим особам.
Девід Родрігес - dribeas
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.