Buscar..


Observaciones

this puntero es una palabra clave para C ++, por lo que no se necesita una biblioteca para implementar esto. Y no olvides que this es un puntero! Así que no puedes hacer:

 this.someMember();

A medida que accede a las funciones o variables de los miembros desde los punteros, utilice el símbolo de flecha -> :

this->someMember();

Otros enlaces útiles para una mejor comprensión de this puntero:

¿Qué es el puntero 'este'?

http://www.geeksforgeeks.org/this-pointer-in-c/

https://www.tutorialspoint.com/cplusplus/cpp_this_pointer.htm

este puntero

Todas las funciones miembro no estáticas tienen un parámetro oculto, un puntero a una instancia de la clase, llamado this ; este parámetro se inserta silenciosamente al principio de la lista de parámetros, y se maneja por completo por el compilador. Cuando se accede a un miembro de la clase dentro de una función miembro, se accede de forma silenciosa a través de this ; esto permite que el compilador utilice una única función miembro no estática para todas las instancias, y permite que una función miembro llame a otras funciones miembro de forma polimórfica.

struct ThisPointer {
    int i;

    ThisPointer(int ii);

    virtual void func();

    int  get_i() const;
    void set_i(int ii);
};
ThisPointer::ThisPointer(int ii) : i(ii) {}
// Compiler rewrites as:
ThisPointer::ThisPointer(int ii) : this->i(ii) {}
// Constructor is responsible for turning allocated memory into 'this'.
// As the constructor is responsible for creating the object, 'this' will not be "fully"
// valid until the instance is fully constructed.

/* virtual */ void ThisPointer::func() {
    if (some_external_condition) {
        set_i(182);
    } else {
        i = 218;
    }
}
// Compiler rewrites as:
/* virtual */ void ThisPointer::func(ThisPointer* this) {
    if (some_external_condition) {
        this->set_i(182);
    } else {
        this->i = 218;
    }
}

int  ThisPointer::get_i() const { return i; }
// Compiler rewrites as:
int  ThisPointer::get_i(const ThisPointer* this) { return this->i; }

void ThisPointer::set_i(int ii) { i = ii; }
// Compiler rewrites as:
void ThisPointer::set_i(ThisPointer* this, int ii) { this->i = ii; }

En un constructor, this se puede usar de manera segura para (implícita o explícitamente) acceder a cualquier campo que ya se haya inicializado, o cualquier campo en una clase principal; a la inversa, el acceso (implícito o explícito) a cualquier campo que aún no se haya inicializado, o cualquier campo en una clase derivada, es inseguro (debido a que la clase derivada aún no se ha construido y, por lo tanto, sus campos no están inicializados ni existen). Tampoco es seguro llamar a las funciones miembro virtuales a través de this en el constructor, ya que no se considerarán las funciones de clase derivadas (debido a que la clase derivada aún no se ha construido y, por lo tanto, su constructor aún no actualiza el vtable).


También tenga en cuenta que, mientras se encuentra en un constructor, el tipo de objeto es el tipo que ese constructor construye. Esto es cierto incluso si el objeto se declara como un tipo derivado. Por ejemplo, en el siguiente ejemplo, ctd_good y ctd_bad son tipo CtorThisBase dentro de CtorThisBase() , y tipo CtorThis dentro de CtorThis() , aunque su tipo canónico es CtorThisDerived . A medida que las clases más derivadas se construyen alrededor de la clase base, la instancia pasa gradualmente a través de la jerarquía de clases hasta que se trata de una instancia completamente construida de su tipo deseado.

class CtorThisBase {
    short s;

  public:
    CtorThisBase() : s(516) {}
};

class CtorThis : public CtorThisBase {
    int i, j, k;

  public:
    // Good constructor.
    CtorThis() : i(s + 42), j(this->i), k(j) {}

    // Bad constructor.
    CtorThis(int ii) : i(ii), j(this->k), k(b ? 51 : -51) {
        virt_func();
    }

    virtual void virt_func() { i += 2; }
};

class CtorThisDerived : public CtorThis {
    bool b;

  public:
    CtorThisDerived()       : b(true) {}
    CtorThisDerived(int ii) : CtorThis(ii), b(false) {}

    void virt_func() override { k += (2 * i); }
};

// ...

CtorThisDerived ctd_good;
CtorThisDerived ctd_bad(3);

Con estas clases y funciones miembro:

  • En el buen constructor, para ctd_good :
    • CtorThisBase se construye completamente cuando se CtorThis constructor CtorThis . Por lo tanto, s encuentra en un estado válido al inicializar i y, por lo tanto, se puede acceder a él.
    • i se inicializa antes de que se alcance j(this->i) . Por lo tanto, i está en un estado válido al inicializar j , y por lo tanto se puede acceder a él.
    • j se inicializa antes de alcanzar k(j) . Por lo tanto, j encuentra en un estado válido al inicializar k , y por lo tanto se puede acceder a él.
  • En el constructor malo, para ctd_bad :
    • k se inicializa después de alcanzar j(this->k) . Por lo tanto, k está en un estado no válido al inicializar j , y acceder a él causa un comportamiento indefinido.
    • CtorThisDerived no se construye hasta después de que CtorThis se construya. Por lo tanto, b encuentra en un estado no válido al inicializar k , y acceder a él provoca un comportamiento indefinido.
    • El objeto ctd_bad sigue siendo un CtorThis hasta que sale CtorThis() , y no se actualiza para utilizar CtorThisDerived vtable 's hasta CtorThisDerived() . Por lo tanto, virt_func() llamará a CtorThis::virt_func() , independientemente de si está destinado a llamar a eso o CtorThisDerived::virt_func() .

Uso de este puntero para acceder a datos de miembros

En este contexto, el uso de this puntero no es completamente necesario, pero hará que su código sea más claro para el lector, al indicar que una función o variable determinada es un miembro de la clase. Un ejemplo en esta situación:

// Example for this pointer
#include <iostream>
#include <string>

using std::cout;
using std::endl;

class Class
{
  public:
    Class();
    ~Class();
    int getPrivateNumber () const;
  private:
    int private_number = 42;
};

Class::Class(){}
Class::~Class(){}

int Class::getPrivateNumber() const
{
    return this->private_number;
}

int main()
{
    Class class_example;
    cout << class_example.getPrivateNumber() << endl;
}

Véalo en acción aquí .

Uso de este puntero para diferenciar entre datos de miembros y parámetros

Esta es una estrategia realmente útil para diferenciar los datos de los miembros de los parámetros ... Tomemos este ejemplo:

// Dog Class Example
#include <iostream>
#include <string>

using std::cout;
using std::endl;

/*
* @class Dog
*   @member name
*       Dog's name
*   @function bark
*       Dog Barks!
*   @function getName
*       To Get Private
*       Name Variable
*/
class Dog
{
 public:
    Dog(std::string name);
    ~Dog();
    void  bark() const;
    std::string  getName() const;
 private:
    std::string name;
};

Dog::Dog(std::string name)
{
    /*
    *  this->name is the
    *  name variable from 
    *  the class dog . and
    *  name is from the 
    *  parameter of the function
    */
    this->name = name; 
}

Dog::~Dog(){}

void Dog::bark() const
{
  cout << "BARK" << endl;   
}

std::string  Dog::getName() const
{
    return this->name;
}


int main()
{
    Dog dog("Max");
    cout << dog.getName() << endl;
    dog.bark();
}

Puedes ver aquí en el constructor ejecutamos lo siguiente:

this->name = name; 

Aquí puede ver que estamos asignando el nombre del parámetro al nombre de la variable privada de la clase Dog (this-> name).

Para ver la salida del código anterior: http://cpp.sh/75r7

este puntero CV-calificadores

this también puede ser calificado como CV, al igual que cualquier otro puntero. Sin embargo, debido a que this parámetro no aparece en la lista de parámetros, se requiere una sintaxis especial para esto; los calificadores cv se enumeran después de la lista de parámetros, pero antes del cuerpo de la función.

struct ThisCVQ {
    void no_qualifier()                {} // "this" is: ThisCVQ*
    void  c_qualifier() const          {} // "this" is: const ThisCVQ*
    void  v_qualifier() volatile       {} // "this" is: volatile ThisCVQ*
    void cv_qualifier() const volatile {} // "this" is: const volatile ThisCVQ*
};

Como this es un parámetro, una función puede ser sobrecargado basado en su this cv-calificador (s) .

struct CVOverload {
    int func()                { return    3; }
    int func() const          { return   33; }
    int func() volatile       { return  333; }
    int func() const volatile { return 3333; }
};

Cuando this es const (incluyendo const volatile ), la función no puede escribir en las variables miembro a través de él, ya sea de forma implícita o explícita. La única excepción a esto son las variables miembro mutable , que pueden escribirse independientemente de la constancia. Debido a esto, const se utiliza para indicar que la función miembro no cambia el estado lógico del objeto (la forma en que el objeto aparece en el mundo exterior), incluso si modifica el estado físico (la forma en que el objeto se ve debajo del capó ).

El estado lógico es la forma en que el objeto aparece ante los observadores externos. No está directamente relacionado con el estado físico y, de hecho, puede que ni siquiera se almacene como estado físico. Mientras los observadores externos no puedan ver ningún cambio, el estado lógico es constante, incluso si volteas cada bit en el objeto.

El estado físico, también conocido como estado a nivel de bits, es la forma en que el objeto se almacena en la memoria. Este es el meollo del objeto, los 1s y 0s en bruto que componen sus datos. Un objeto solo es físicamente constante si su representación en la memoria nunca cambia.

Tenga en cuenta que C ++ basa la const en el estado lógico, no en el estado físico.

class DoSomethingComplexAndOrExpensive {
    mutable ResultType cached_result;
    mutable bool state_changed;

    ResultType calculate_result();
    void modify_somehow(const Param& p);

    // ...

  public:
    DoSomethingComplexAndOrExpensive(Param p) : state_changed(true) {
        modify_somehow(p);
    }

    void change_state(Param p) {
        modify_somehow(p);
        state_changed = true;
    }

    // Return some complex and/or expensive-to-calculate result.
    // As this has no reason to modify logical state, it is marked as "const".
    ResultType get_result() const;
};
ResultType DoSomethingComplexAndOrExpensive::get_result() const {
    // cached_result and state_changed can be modified, even with a const "this" pointer.
    // Even though the function doesn't modify logical state, it does modify physical state
    //  by caching the result, so it doesn't need to be recalculated every time the function
    //  is called.  This is indicated by cached_result and state_changed being mutable.

    if (state_changed) {
        cached_result = calculate_result();
        state_changed = false;
    }

    return cached_result;
}

Tenga en cuenta que, si bien técnicamente podría usar const_cast en this para que no esté calificado como const_cast , realmente, REALMENTE no debería, y debería usar mutable lugar. Un const_cast puede invocar un comportamiento indefinido cuando se usa en un objeto que en realidad es const , mientras que mutable está diseñado para ser seguro de usar. Sin embargo, es posible que se encuentre con este código extremadamente antiguo.

Una excepción a esta regla es la definición de accesores no calificados para CV en términos de const acceso; como se garantiza que el objeto no será const si se llama a la versión no calificada de CV, no hay riesgo de UB.

class CVAccessor {
    int arr[5];

  public:
    const int& get_arr_element(size_t i) const { return arr[i]; }

    int& get_arr_element(size_t i) {
        return const_cast<int&>(const_cast<const CVAccessor*>(this)->get_arr_element(i));
    }
};

Esto evita la duplicación innecesaria de código.


Al igual que con los punteros regulares, si this es volatile (incluyendo const volatile ), se carga desde la memoria cada vez que se accede, en lugar de ser almacenado en caché. Esto tiene los mismos efectos en la optimización que declarar cualquier otro puntero volatile , por lo que se debe tener cuidado.


Tenga en cuenta que si una instancia es cv-cualificado, las únicas funciones miembro que se permite el acceso a funciones son miembros cuyas this puntero es al menos tan cv-calificada como la propia instancia:

  • Las instancias no cv pueden acceder a cualquier función miembro.
  • const instancias const pueden acceder a const volatile funciones const y const volatile .
  • volatile instancias volatile pueden acceder a funciones volatile y const volatile .
  • const volatile instancias const volatile pueden acceder a funciones const volatile .

Este es uno de los principios clave de la corrección const .

struct CVAccess {
    void    func()                {}
    void  func_c() const          {}
    void  func_v() volatile       {}
    void func_cv() const volatile {}
};

CVAccess cva;
cva.func();    // Good.
cva.func_c();  // Good.
cva.func_v();  // Good.
cva.func_cv(); // Good.

const CVAccess c_cva;
c_cva.func();    // Error.
c_cva.func_c();  // Good.
c_cva.func_v();  // Error.
c_cva.func_cv(); // Good.

volatile CVAccess v_cva;
v_cva.func();    // Error.
v_cva.func_c();  // Error.
v_cva.func_v();  // Good.
v_cva.func_cv(); // Good.

const volatile CVAccess cv_cva;
cv_cva.func();    // Error.
cv_cva.func_c();  // Error.
cv_cva.func_v();  // Error.
cv_cva.func_cv(); // Good.

este puntero ref-calificadores

C ++ 11

De manera similar a this calificadores de CV, también podemos aplicar ref-calificadores a *this . Los ref-calificadores se utilizan para elegir entre semántica de referencia normal y rvalor, lo que permite al compilador usar la semántica de copiar o mover según sea más apropiado, y se aplican a *this lugar de this .

Tenga en cuenta que a pesar de los ref-calificadores que usan la sintaxis de referencia, this sigue siendo un puntero. También tenga en cuenta que los ref-calificadores no cambian realmente el tipo de *this ; es más fácil describir y entender sus efectos mirándolos como si lo hicieran.

struct RefQualifiers {
    std::string s;

    RefQualifiers(const std::string& ss = "The nameless one.") : s(ss) {}

    // Normal version.
    void func() &  { std::cout << "Accessed on normal instance "    << s << std::endl; }
    // Rvalue version.
    void func() && { std::cout << "Accessed on temporary instance " << s << std::endl; }

    const std::string& still_a_pointer() &  { return this->s; }
    const std::string& still_a_pointer() && { this->s = "Bob"; return this->s; }
};

// ...

RefQualifiers rf("Fred");
rf.func();              // Output:  Accessed on normal instance Fred
RefQualifiers{}.func(); // Output:  Accessed on temporary instance The nameless one

Una función miembro no puede tener sobrecargas con y sin ref-calificadores; El programador tiene que elegir entre uno u otro. Afortunadamente, cv-qualifiers se puede utilizar junto con ref-qualifiers, lo que permite seguir las reglas de corrección de const .

struct RefCV {
    void func() &                {}
    void func() &&               {}
    void func() const&           {}
    void func() const&&          {}
    void func() volatile&        {}
    void func() volatile&&       {}
    void func() const volatile&  {}
    void func() const volatile&& {}
};


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow