Wann sollte ich explizit this->member
in eine Methode einer Klasse schreiben ?
research.att.com/~bs/
jetzt so aus stroustrup.com
. Neuer Link: stroustrup.com/bs_faq2.html#this
Wann sollte ich explizit this->member
in eine Methode einer Klasse schreiben ?
research.att.com/~bs/
jetzt so aus stroustrup.com
. Neuer Link: stroustrup.com/bs_faq2.html#this
Antworten:
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, i
ist 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
}
};
i
es in nicht existiert A
. Könnte ich ein Beispiel bekommen?
template<> struct A<float> { float x; };
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
Es gibt mehrere Gründe, warum Sie den this
Zeiger möglicherweise explizit verwenden müssen.
Obwohl ich es normalerweise nicht besonders mag, habe ich gesehen, dass andere dies benutzen -> nur um Hilfe von Intellisense zu bekommen!
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;
}
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.
const
fü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));
)
Es gibt wenige Fälle, in denen die Verwendung this
verwendet werden muss , und es gibt andere Fälle, in denen die Verwendung des this
Zeigers 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 this
von 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 mName
für Mitglieder und name
für Einheimische.
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.
this->
, die trivial zu vermeiden ist (geben Sie der lokalen Variablen einfach einen anderen Namen). Alle wirklich interessanten Verwendungen von this
werden in dieser Antwort nicht einmal erwähnt.
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.
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;
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.
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;
}
};
Der Hauptzweck (oder ich kann sagen, der einzige) des this
Zeigers 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 this
Zeigers 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
}
};
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;
length
veränderlich machen oder es sogar in eine verschachtelte Struktur einfügen. Konstanz wegzuwerfen ist fast nie eine gute Idee.
const
Mitgliedsfunktionen geändert werden soll, sollte es das sein mutable
. Andernfalls machen Sie das Leben für Sie und andere Betreuer komplizierter.