¿Cuándo debería escribir explícitamente this->member
en un método de una clase?
research.att.com/~bs/
es ahora stroustrup.com
. Nuevo enlace: stroustrup.com/bs_faq2.html#this
¿Cuándo debería escribir explícitamente this->member
en un método de una clase?
research.att.com/~bs/
es ahora stroustrup.com
. Nuevo enlace: stroustrup.com/bs_faq2.html#this
Respuestas:
Por lo general, no this->
es necesario , está implícito.
A veces, existe una ambigüedad de nombre, donde se puede usar para eliminar la ambigüedad de los miembros de la clase y las variables locales. Sin embargo, aquí hay un caso completamente diferente donde this->
se requiere explícitamente.
Considere el siguiente 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();
}
Si omite this->
, el compilador no sabe cómo tratar i
, ya que puede existir o no en todas las instancias de A
. Para decirle que i
efectivamente es miembro de A<T>
, para cualquiera T
, this->
se requiere el prefijo.
Nota: aún es posible omitir el this->
prefijo 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
}
};
i
podría no existir en A
. ¿Podría darme un ejemplo?
template<> struct A<float> { float x; };
Si declara una variable local en un método con el mismo nombre que un miembro existente, tendrá que usar this-> var para acceder al miembro de la clase en lugar de a la variable 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();
}
huellas dactilares:
5
4
Hay varias razones por las que podría necesitar usar this
puntero de forma explícita.
Aunque generalmente no me gusta en particular, he visto a otros usar esto-> ¡simplemente para obtener ayuda de intellisense!
Algunos estándares de codificación utilizan el enfoque (2), ya que afirman que hace que el código sea más fácil de leer.
Ejemplo:
suponga que MyClass tiene una variable miembro llamada 'recuento'
void MyClass::DoSomeStuff(void)
{
int count = 0;
.....
count++;
this->count = count;
}
Otro caso es cuando se invocan operadores. Por ejemplo, en lugar de
bool Type::operator!=(const Type& rhs)
{
return !operator==(rhs);
}
puedes decir
bool Type::operator!=(const Type& rhs)
{
return !(*this == rhs);
}
Cuál podría ser más legible. Otro ejemplo es el de copiar e intercambiar:
Type& Type::operator=(const Type& rhs)
{
Type temp(rhs);
temp.swap(*this);
}
No sé por qué no está escrito, swap(temp)
pero esto parece ser común.
const
función de miembro de manera temporal ( Type(rhs).swap(*this);
es legal y correcto), pero una lata temporal se une a un parámetro de referencia no constante (rechazos del compilador swap(Type(rhs));
, así como this->swap(Type(rhs));
)
Hay pocos casos en los this
que se debe usar el uso, y hay otros en los que se debe usar elthis
puntero es una forma de resolver un problema.
1) Alternativas disponibles : para resolver la ambigüedad entre las variables locales y los miembros de la clase, como lo ilustra @ASk .
2) Sin alternativa: para devolver un puntero o una referencia this
desde una función miembro. Esto se hace con frecuencia (y se debe hacer) cuando se sobrecarga operator+
, operator-
, operator=
, etc:
class Foo
{
Foo& operator=(const Foo& rhs)
{
return * this;
}
};
Hacer esto permite un modismo conocido como " encadenamiento de métodos ", donde se realizan varias operaciones en un objeto en una línea de código. Como:
Student st;
st.SetAge (21).SetGender (male).SetClass ("C++ 101");
Algunos consideran esta consistencia, otros la consideran una abominación. Cuente conmigo en el último grupo.
3) Sin alternativa: Para resolver nombres en tipos dependientes. Esto surge cuando se utilizan plantillas, como en este ejemplo:
#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 disponibles: como parte del estilo de codificación, para documentar qué variables son variables miembro en lugar de variables locales. Prefiero un esquema de nomenclatura diferente donde los miembros varibales nunca pueden tener el mismo nombre que los locales. Actualmente estoy usando mName
para miembros y name
para locales.
Solo tiene que usar this-> si tiene un símbolo con el mismo nombre en dos espacios de nombres potenciales. Toma por ejemplo:
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
}
En los puntos interesantes del código, la referencia a myVar se referirá a la myVar local (parámetro o variable). Para acceder al miembro de la clase también llamado myVar, necesita usar explícitamente "this->".
this->
que es trivial de evitar (simplemente dé a la variable local un nombre diferente). Todos los usos realmente interesantes de this
ni siquiera se mencionan en esta respuesta.
Los otros usos para esto (como pensé cuando leí el resumen y la mitad de la pregunta ...), sin tener en cuenta (mal) la desambiguación de nombres en otras respuestas, son si desea lanzar el objeto actual, vincularlo en un objeto de función o utilícelo con un puntero a miembro.
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)();
}
}
Espero que ayude a mostrar otros usos de esto además de este-> miembro.
Necesita usar this
para eliminar la ambigüedad entre parámetros / variables locales y variables miembro.
class Foo
{
protected:
int myX;
public:
Foo(int myX)
{
this->myX = myX;
}
};
El principal (o puedo decir, el único) propósito del this
puntero es que apunte al objeto utilizado para invocar una función miembro.
Con base en este propósito, podemos tener algunos casos en los que solo el uso del this
puntero puede resolver el problema.
Por ejemplo, tenemos que devolver el objeto de invocación en una función miembro con argumento es un mismo objeto de clase:
class human {
...
human & human::compare(human & h){
if (condition)
return h; // argument object
else
return *this; // invoking object
}
};
Encontré otro caso interesante de uso explícito del puntero "this" en el libro Effective C ++.
Por ejemplo, digamos que tiene una función constante como
unsigned String::length() const
No desea calcular la longitud de String para cada llamada, por lo tanto, desea almacenarlo en caché haciendo algo como
unsigned String::length() const
{
if(!lengthInitialized)
{
length = strlen(data);
lengthInitialized = 1;
}
}
Pero esto no se compilará, está cambiando el objeto en una función constante.
El truco para resolver esto requiere fundición esta a un no-const esto :
String* const nonConstThis = (String* const) this;
Entonces, podrás hacer arriba
nonConstThis->lengthInitialized = 1;
length
mutable, o incluso ponerlo en una estructura anidada. Dejar de lado la consistencia casi nunca es una buena idea.
const
funciones de miembro, debería serlo mutable
. De lo contrario, está complicando la vida para usted y otros mantenedores.