Wann sollte ich den Zeiger "this" explizit verwenden?


97

Wann sollte ich explizit this->memberin eine Methode einer Klasse schreiben ?


16
Ich bin mir sicher, dass dies ein Betrug ist, aber es ist natürlich nicht durchsuchbar. Nicht zum ersten Mal wünschte ich mir, dieser Zeiger würde Selbst heißen!

5
Nicht nur das, ich wünschte, es wäre eine Referenz.
Rlbond

2
Gleich. : | Hier ist übrigens der
Grund

11
Diese Methode funktioniert offensichtlich nicht, wenn die Person die Antwort nicht kennt.
Fragen Sie am

3
@ JohnH.: Hm, sieht research.att.com/~bs/jetzt so aus stroustrup.com. Neuer Link: stroustrup.com/bs_faq2.html#this
GManNickG

Antworten:


118

Normalerweise müssen Sie nicht, this-> ist impliziert.

Manchmal gibt es eine Namensmehrdeutigkeit, mit der Klassenmitglieder und lokale Variablen eindeutig unterschieden werden können. Hier ist jedoch ein ganz anderer Fallthis-> ausdrücklich erforderlich ist.

Betrachten Sie den folgenden Code:

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

Wenn Sie dies weglassen this->, weiß der Compiler nicht, wie er zu behandeln ist i, da er möglicherweise in allen Instanziierungen von vorhanden ist oder nicht A. Um es zu sagen, iist das in der Tat ein Mitglied von A<T>, für jedenT das this->Präfix erforderlich.

Hinweis: Es ist weiterhin möglich, das this->Präfix wegzulassen, indem Sie Folgendes verwenden:

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
Gute Verwendung der Deklaration :)
Faisal Vali

3
Dies ist ein besonders schlimmer Fall. Ich bin schon einmal davon gebissen worden.
Jason Baker

5
Dies mag eine dumme Frage sein, aber ich verstehe nicht, warum ies in nicht existiert A. Könnte ich ein Beispiel bekommen?
Cam Jackson

1
@CamJackson Ich habe den Code in Visual Studio ausprobiert. Die Ergebnisse sind die gleichen, egal ob "this->" existiert oder nicht. Irgendeine Idee?
Peng Zhang

8
@ CamJackson: Man kann Klassen auf Typ spezialisieren:template<> struct A<float> { float x; };
Macke

31

Wenn Sie eine lokale Variable in einer Methode mit demselben Namen wie ein vorhandenes Mitglied deklarieren, müssen Sie this-> var verwenden, um auf das Klassenmitglied anstelle der lokalen Variablen zuzugreifen.

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

Drucke:

5
4


1
Ich würde besser cout << A :: a << endl verwenden; stattdessen. `` this "ist in diesem Fall unwichtig.
siddhant3s

3
Ich würde lieber den Namenskonflikt mit Konventionen wie "m_a" oder "a_" vermeiden.
Tom

19

Es gibt mehrere Gründe, warum Sie den thisZeiger möglicherweise explizit verwenden müssen.

  • Wenn Sie einen Verweis auf Ihr Objekt an eine Funktion übergeben möchten.
  • Wenn es ein lokal deklariertes Objekt mit demselben Namen wie das Mitgliedsobjekt gibt.
  • Wenn Sie versuchen, auf Mitglieder abhängiger Basisklassen zuzugreifen .
  • Einige Leute bevorzugen die Notation, um Mitgliederzugriffe in ihrem Code visuell eindeutig zu machen.

7

Obwohl ich es normalerweise nicht besonders mag, habe ich gesehen, dass andere dies benutzen -> nur um Hilfe von Intellisense zu bekommen!


6
  1. Wo eine Mitgliedsvariable von einer lokalen Variablen ausgeblendet würde
  2. Wenn Sie nur explizit klarstellen möchten, dass Sie eine Instanzmethode / -variable aufrufen


Einige Codierungsstandards verwenden Ansatz (2), da sie behaupten, dass er den Code leichter lesbar macht.

Beispiel:
Angenommen, MyClass hat eine Mitgliedsvariable namens 'count'

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

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

5

Ein anderer Fall ist das Aufrufen von Operatoren. ZB statt

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

Sie können sagen

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

Welches könnte besser lesbar sein. Ein weiteres Beispiel ist das Kopieren und Austauschen:

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

Ich weiß nicht, warum es nicht geschrieben ist, swap(temp)aber das scheint üblich zu sein.


Beachten Sie in Ihrem letzten Fall, dass Sie eine Nichtmitgliedsfunktion constfür eine temporäre Funktion aufrufen können ( Type(rhs).swap(*this);ist legal und korrekt), eine temporäre Funktion jedoch nicht an einen nicht konstanten Referenzparameter binden kann (Compiler lehnt dies swap(Type(rhs));ebenfalls ab this->swap(Type(rhs));)
Ben Voigt

5

Es gibt wenige Fälle, in denen die Verwendung this verwendet werden muss , und es gibt andere Fälle, in denen die Verwendung des thisZeigers eine Möglichkeit ist, ein Problem zu lösen.

1) Verfügbare Alternativen : Zum Auflösen von Mehrdeutigkeiten zwischen lokalen Variablen und Klassenmitgliedern, wie in @ASk dargestellt .

2) Keine Alternative: Um einen Zeiger oder Verweis thisvon einer Mitgliedsfunktion zurückzugeben. Dies wird häufig getan (und sollte getan werden) , wenn eine Überlastung operator+, operator-, operator=, etc:

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

Dies ermöglicht eine Redewendung, die als " Methodenverkettung " bezeichnet wird und bei der Sie mehrere Operationen an einem Objekt in einer Codezeile ausführen. Sowie:

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

Einige halten dies für eine Abscheulichkeit, andere für einen Greuel. Zähle mich in der letzteren Gruppe.

3) Keine Alternative: Zum Auflösen von Namen in abhängigen Typen. Dies tritt auf, wenn Vorlagen verwendet werden, wie in diesem Beispiel:

#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) Verfügbare Alternativen: Als Teil des Codierungsstils, um zu dokumentieren, welche Variablen im Gegensatz zu lokalen Variablen Mitgliedsvariablen sind. Ich bevorzuge ein anderes Namensschema, bei dem Mitgliedsvariablen niemals den gleichen Namen wie Einheimische haben können. Derzeit benutze ich mNamefür Mitglieder und namefür Einheimische.


4

Sie müssen dies-> nur verwenden, wenn Sie ein Symbol mit demselben Namen in zwei möglichen Namespaces haben. Nehmen Sie zum Beispiel:

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
}

An den interessanten Stellen im Code bezieht sich die Bezugnahme auf myVar auf die lokale (Parameter oder Variable) myVar. Um auf das Klassenmitglied zuzugreifen, das auch myVar genannt wird, müssen Sie explizit "this->" verwenden.


Dies ist die einzige Verwendung this->, die trivial zu vermeiden ist (geben Sie der lokalen Variablen einfach einen anderen Namen). Alle wirklich interessanten Verwendungen von thiswerden in dieser Antwort nicht einmal erwähnt.
cmaster

4

Die andere Verwendung dafür (wie ich dachte, als ich die Zusammenfassung und die Hälfte der Frage gelesen habe ...), die (schlechte) Namensdisambiguierung in anderen Antworten außer Acht lässt, ist, wenn Sie das aktuelle Objekt umwandeln möchten, es in ein Funktionsobjekt zu binden oder verwenden Sie es mit einem Zeiger auf ein Mitglied.

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

Bindung

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

Ich hoffe, es hilft, andere Verwendungszwecke als nur dieses-> Mitglied aufzuzeigen.


3

Sie müssen verwenden this, um zwischen Parametern / lokalen Variablen und Mitgliedsvariablen zu unterscheiden.

class Foo
{
protected:
  int myX;

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

2
Nein, Sie nicht brauchen , es können Sie verwenden es. Sie können auch einen anderen Namen für das Funktionsargument verwenden, was den Vorteil hat, dass nicht zwei Entitäten mit demselben Namen vorhanden sind.
cmaster

3

Der Hauptzweck (oder ich kann sagen, der einzige) des thisZeigers besteht darin, dass er auf das Objekt zeigt, mit dem eine Elementfunktion aufgerufen wird.

Basierend auf diesem Zweck können wir einige Fälle haben, in denen nur die Verwendung eines thisZeigers das Problem lösen kann.

Zum Beispiel müssen wir das aufrufende Objekt in einer Mitgliedsfunktion mit Argument zurückgeben, das dasselbe Klassenobjekt ist:

class human {

... 

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

2

Ich fand einen weiteren interessanten Fall der expliziten Verwendung des Zeigers "this" im Effective C ++ - Buch.

Angenommen, Sie haben eine const-Funktion wie

  unsigned String::length() const

Sie möchten nicht die Länge des Strings für jeden Aufruf berechnen, daher möchten Sie ihn zwischenspeichern, indem Sie so etwas tun

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

Dies wird jedoch nicht kompiliert - Sie ändern das Objekt in einer const-Funktion.

Der Trick zu lösen dies erfordert Gießen dies zu einem nicht-const diese :

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

Dann können Sie oben tun

  nonConstThis->lengthInitialized = 1;

3
Oder Sie können lengthveränderlich machen oder es sogar in eine verschachtelte Struktur einfügen. Konstanz wegzuwerfen ist fast nie eine gute Idee.
Richard J. Ross III

3
Bitte nicht. Wenn das Mitglied von den constMitgliedsfunktionen geändert werden soll, sollte es das sein mutable. Andernfalls machen Sie das Leben für Sie und andere Betreuer komplizierter.
David Rodríguez - Dribeas
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.