Quando devo fazer uso explícito do ponteiro `this`?

97

Quando devo escrever explicitamente this->memberem um método de uma classe?

dmckee --- gatinho ex-moderador
fonte
16
Tenho certeza de que isso é um idiota, mas é claro que é insondável. Não pela primeira vez, gostaria que este ponteiro se chamasse self!
5
Não só isso, eu gostaria que fosse uma referência.
rlbond
2
Mesmo. : | A propósito, aqui está o motivo: research.att.com/~bs/bs_faq2.html#this
GManNickG
11
Esse método obviamente não funciona se a pessoa não souber a resposta.
Pergunta
3
@JohnH .: Hm, parece que research.att.com/~bs/agora stroustrup.com. Novo link: stroustrup.com/bs_faq2.html#this
GManNickG

Respostas:

118

Normalmente, você não precisa, this->está implícito.

Às vezes, há uma ambigüidade de nome, onde pode ser usado para desambiguar membros de classe e variáveis ​​locais. No entanto, aqui está um caso completamente diferente em que this->é explicitamente necessário.

Considere o seguinte código:

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

Se omitir this->, o compilador não saberá como tratar i, pois pode ou não existir em todas as instanciações de A. Para dizer que ié de fato um membro de A<T>, para qualquer T, o this->prefixo é necessário.

Nota: ainda é possível omitir o this->prefixo usando:

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
    }

};
ASk
fonte
8
Bom uso de declaração :)
Faisal Vali
3
Este é um caso particularmente desagradável. Já fui mordido por isso antes.
Jason Baker
5
Pode ser uma pergunta boba, mas não entendo por ique não existe em A. Posso obter um exemplo?
Cam Jackson
1
@CamJackson Tentei o código no Visual Studio. os resultados são os mesmos, não importa "this->" existia ou não. Qualquer ideia?
Peng Zhang de
8
@CamJackson: Pode-se especializar aulas no tipo:template<> struct A<float> { float x; };
Macke
31

Se você declarar uma variável local em um método com o mesmo nome de um membro existente, você terá que usar this-> var para acessar o membro da classe em vez da variável local.

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

estampas:

5
4

PaV
fonte
1
Seria melhor usar cout << A :: a << endl; em vez de. `` isto "não é importante neste caso.
siddhant3s
3
Eu prefiro apenas evitar o conflito de nomes com convenções como "m_a" ou "a_".
Tom
19

Existem vários motivos pelos quais você pode precisar usar o thisponteiro explicitamente.

  • Quando você deseja passar uma referência ao seu objeto para alguma função.
  • Quando há um objeto declarado localmente com o mesmo nome do objeto membro.
  • Quando você está tentando acessar membros de classes base dependentes .
  • Algumas pessoas preferem a notação para desambiguar visualmente os acessos de membros em seu código.
avakar
fonte
7

Embora eu geralmente não goste, tenho visto outras pessoas usarem isso-> simplesmente para obter ajuda do intellisense!

Brad Robinson
fonte
6
  1. Onde uma variável de membro seria escondida por uma variável local
  2. Se você quiser apenas deixar explicitamente claro que está chamando um método / variável de instância


Alguns padrões de codificação usam a abordagem (2), pois afirmam que torna o código mais fácil de ler.

Exemplo:
suponha que MyClass tenha uma variável de membro chamada 'count'

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

   .....
   count++;
   this->count = count;
}
Zebrabox
fonte
5

Um outro caso é ao invocar operadores. Por exemplo, em vez de

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

Você pode dizer

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

O que pode ser mais legível. Outro exemplo é o copiar e trocar:

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

Não sei por que não está escrito, swap(temp)mas parece ser comum.

rlbond
fonte
Em seu último caso, observe que você pode chamar uma constfunção não membro em um temporário ( Type(rhs).swap(*this);é legal e correto), mas um temporário não pode vincular a um parâmetro de referência não const (o compilador rejeita swap(Type(rhs));também this->swap(Type(rhs));)
Ben Voigt
5

Existem alguns casos em que o uso this deve ser usado e há outros em que usar o thisponteiro é uma maneira de resolver um problema.

1) Alternativas disponíveis : Para resolver a ambigüidade entre as variáveis ​​locais e os membros da classe, conforme ilustrado por @ASk .

2) Sem alternativa: Para retornar um ponteiro ou referência thisde uma função de membro. Isso é feito com freqüência (e deve ser feito) quando a sobrecarga operator+, operator-, operator=, etc:

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

Isso permite um idioma conhecido como " encadeamento de método ", onde você executa várias operações em um objeto em uma linha de código. Tal como:

Student st;
st.SetAge (21).SetGender (male).SetClass ("C++ 101");

Alguns consideram isso consistente, outros consideram uma abominação. Conte comigo no último grupo.

3) Sem alternativa: para resolver nomes em tipos dependentes. Isso surge ao usar modelos, como neste exemplo:

#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) Alternativas disponíveis: Como parte do estilo de codificação, para documentar quais variáveis ​​são variáveis ​​de membro em oposição a variáveis ​​locais. Eu prefiro um esquema de nomenclatura diferente, onde os membros varibales nunca podem ter o mesmo nome dos locais. Atualmente estou usando mNamepara membros ename locais.

John Dibling
fonte
4

Você só precisa usar this-> se tiver um símbolo com o mesmo nome em dois namespaces potenciais. Considere por exemplo:

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
}

Nos pontos interessantes do código, referir-se a myVar se referirá ao local (parâmetro ou variável) myVar. Para acessar o membro da classe também chamado myVar, você precisa usar explicitamente "this->".

Joe Schneider
fonte
Esse é o único uso de this->que é trivial evitar (basta dar um nome diferente à variável local). Todos os usos realmente interessantes de thisnem mesmo são mencionados por esta resposta.
cmaster - restabelecer monica
4

Os outros usos para isso (como eu pensei quando li o resumo e metade da pergunta ...), desconsiderando a desambiguação de nomenclatura (ruim) em outras respostas, são se você deseja lançar o objeto atual, vinculá-lo a um objeto de função ou use-o com um ponteiro para membro.

Casts

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

Obrigatório

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

Espero que ajude a mostrar outros usos disso além de apenas este-> membro.

Macke
fonte
3

Você precisa usar thispara desambiguar entre parâmetros / variáveis ​​locais e variáveis ​​de membro.

class Foo
{
protected:
  int myX;

public:
  Foo(int myX)
  {
    this->myX = myX; 
  }
};
Brian R. Bondy
fonte
2
Não, você não precisa disso, você pode usar . Você também pode usar um nome diferente para o argumento da função, que tem a vantagem de não ter duas entidades com o mesmo nome.
cmaster - restabelecer monica
3

O principal (ou posso dizer, o único) objetivo do thisponteiro é apontar para o objeto usado para invocar uma função de membro.

Com base neste propósito, podemos ter alguns casos em que apenas o uso de thisponteiro pode resolver o problema.

Por exemplo, temos que retornar o objeto de chamada em uma função-membro com argumento é um mesmo objeto de classe:

class human {

... 

human & human::compare(human & h){
    if (condition)
        return h;       // argument object
    else 
        return *this;   // invoking object
    }
};
Trevor
fonte
2

Eu encontrei outro caso interessante de uso explícito do ponteiro "this" no livro Effective C ++.

Por exemplo, digamos que você tenha uma função const como

  unsigned String::length() const

Você não quer calcular o comprimento da String para cada chamada, portanto, você deseja armazená-la em cache fazendo algo como

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

Mas isso não vai compilar - você está mudando o objeto em uma função const.

O truque para resolver isso requer lançando este a um não-const esta :

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

Então, você será capaz de fazer acima

  nonConstThis->lengthInitialized = 1;
Ariel
fonte
3
Ou você pode torná-lo lengthmutável ou até mesmo colocá-lo em uma estrutura aninhada. Jogar fora a constância quase nunca é uma boa ideia.
Richard J. Ross III
3
Por favor, não. Se o membro tiver que ser mudado de constfunções de membro, deve ser mutable. Caso contrário, você estará tornando a vida mais complicada para você e outros mantenedores.
David Rodríguez - dribeas