Kiedy powinienem jawnie pisać this->member
w metodzie klasy?
research.att.com/~bs/
to , że jest teraz stroustrup.com
. Nowy link: stroustrup.com/bs_faq2.html#this
Kiedy powinienem jawnie pisać this->member
w metodzie klasy?
research.att.com/~bs/
to , że jest teraz stroustrup.com
. Nowy link: stroustrup.com/bs_faq2.html#this
Odpowiedzi:
Zwykle nie musisz tego robić this->
.
Czasami występuje niejednoznaczność nazwy, w której można ją wykorzystać do ujednoznacznienia członków klasy i zmiennych lokalnych. Jednak tutaj jest zupełnie inny przypadek, w którym this->
jest to wyraźnie wymagane.
Rozważ następujący kod:
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();
}
Jeśli pominiesz this->
, kompilator nie będzie wiedział, jak traktować i
, ponieważ może istnieć lub nie we wszystkich wystąpieniach A
. Aby stwierdzić, że i
jest członkiem A<T>
, dla każdego T
, this->
wymagany jest przedrostek.
Uwaga: nadal można pominąć this->
prefiks, używając:
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
może nie istnieć w A
. Czy mógłbym dostać przykład?
template<> struct A<float> { float x; };
Jeśli deklarujesz zmienną lokalną w metodzie o tej samej nazwie, co istniejący element członkowski, będziesz musiał użyć this-> var, aby uzyskać dostęp do elementu członkowskiego klasy zamiast do zmiennej lokalnej.
#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();
}
wydruki:
5
4
Istnieje kilka powodów, dla których może być konieczne this
jawne użycie wskaźnika.
Chociaż zwykle mi się to nie podoba, widziałem, jak inni używają tego -> po prostu do uzyskania pomocy od inteligencji!
Niektóre standardy kodowania używają podejścia (2), ponieważ twierdzą, że ułatwia to odczytanie kodu.
Przykład:
Załóżmy, że MyClass ma zmienną składową o nazwie „count”
void MyClass::DoSomeStuff(void)
{
int count = 0;
.....
count++;
this->count = count;
}
Jest kilka przypadków, w których this
należy użyć using , a są inne, w których użycie this
wskaźnika jest jednym ze sposobów rozwiązania problemu.
1) Dostępne alternatywy : Aby rozwiązać niejednoznaczność między zmiennymi lokalnymi a składowymi klas, jak pokazano na @ASk .
2) Brak alternatywy: aby zwrócić wskaźnik lub odwołanie do this
funkcji składowej. Jest to często zrobić (i powinno być zrobione) przy przeciążeniu operator+
, operator-
, operator=
itp:
class Foo
{
Foo& operator=(const Foo& rhs)
{
return * this;
}
};
W ten sposób można zastosować idiom zwany „ łączeniem metod ”, w którym wykonujesz kilka operacji na obiekcie w jednej linii kodu. Jak na przykład:
Student st;
st.SetAge (21).SetGender (male).SetClass ("C++ 101");
Niektórzy uważają ten błąd, inni uważają to za obrzydliwość. Zalicz mnie do tej drugiej grupy.
3) Brak alternatywy: rozwiązywanie nazw w typach zależnych. Pojawia się podczas korzystania z szablonów, jak w tym przykładzie:
#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) Dostępne alternatywy: jako część stylu kodowania, do udokumentowania, które zmienne są zmiennymi składowymi, a które zmiennymi lokalnymi. Wolę inny schemat nazewnictwa, w którym zmienne składowe nigdy nie mogą mieć takiej samej nazwy jak lokalni. Obecnie używam mName
dla członków i name
dla mieszkańców.
Jeszcze jeden przypadek dotyczy wywoływania operatorów. Np. Zamiast
bool Type::operator!=(const Type& rhs)
{
return !operator==(rhs);
}
możesz powiedzieć
bool Type::operator!=(const Type& rhs)
{
return !(*this == rhs);
}
Które mogłoby być bardziej czytelne. Innym przykładem jest kopiuj i zamień:
Type& Type::operator=(const Type& rhs)
{
Type temp(rhs);
temp.swap(*this);
}
Nie wiem, dlaczego nie jest napisane, swap(temp)
ale wydaje się, że jest to powszechne.
const
funkcji członka na tymczasowy ( Type(rhs).swap(*this);
jest legalne i zgodne z prawdą), ale przejściowych nie wiążą się z const parametru odniesienia (odrzuty kompilatora swap(Type(rhs));
, jak również this->swap(Type(rhs));
)
Musisz użyć this-> tylko wtedy, gdy masz symbol o tej samej nazwie w dwóch potencjalnych przestrzeniach nazw. Weź na przykład:
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
}
W interesujących punktach kodu odwołanie do myVar będzie odnosić się do lokalnego (parametru lub zmiennej) myVar. Aby uzyskać dostęp do elementu klasy zwanego również myVar, musisz jawnie użyć „this->”.
this->
którego należy unikać (wystarczy nadać zmiennej lokalnej inną nazwę). this
Ta odpowiedź nie wspomina nawet o wszystkich naprawdę interesujących zastosowaniach .
Inne zastosowania do tego (jak myślałem, kiedy czytałem podsumowanie i połowę pytania ....), Pomijając (złe) ujednoznacznienie nazewnictwa w innych odpowiedziach, to jeśli chcesz rzutować bieżący obiekt, powiązaj go w obiekcie funkcji lub użyj go ze wskaźnikiem do elementu członkowskiego.
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)();
}
}
Mam nadzieję, że pomoże to pokazać inne zastosowania tego niż tylko ten-> członek.
Musisz użyć, this
aby rozróżnić parametry / zmienne lokalne i zmienne składowe.
class Foo
{
protected:
int myX;
public:
Foo(int myX)
{
this->myX = myX;
}
};
Głównym (lub mogę powiedzieć, jedynym) celem this
wskaźnika jest wskazanie obiektu używanego do wywołania funkcji składowej.
Bazując na tym celu, możemy mieć przypadki, w których tylko użycie this
wskaźnika może rozwiązać problem.
Na przykład, musimy zwrócić obiekt wywołujący w funkcji składowej, której argument jest tym samym obiektem klasy:
class human {
...
human & human::compare(human & h){
if (condition)
return h; // argument object
else
return *this; // invoking object
}
};
Znalazłem inny interesujący przypadek jawnego użycia wskaźnika „this” w książce Effective C ++.
Na przykład, powiedzmy, że masz funkcję const, taką jak
unsigned String::length() const
Nie chcesz obliczać długości łańcucha dla każdego wywołania, dlatego chcesz go buforować, robiąc coś takiego
unsigned String::length() const
{
if(!lengthInitialized)
{
length = strlen(data);
lengthInitialized = 1;
}
}
Ale to się nie skompiluje - zmieniasz obiekt w funkcji const.
Sztuczka, aby rozwiązać ten problem, wymaga rzutowania tego na inny element niż const this :
String* const nonConstThis = (String* const) this;
Wtedy będziesz mógł zrobić to powyżej
nonConstThis->lengthInitialized = 1;
length
mutable, a nawet umieścić go w zagnieżdżonej strukturze. Rzucanie konsternacji prawie nigdy nie jest dobrym pomysłem.
const
funkcji składowych, powinien być mutable
. W przeciwnym razie utrudnisz życie sobie i innym opiekunom.