Sök…


Anmärkningar

Den this pekaren är ett nyckelord för C ++ strider därför finns det inget bibliotek som behövs för att genomföra detta. Och glöm inte att det this är en pekare! Så du kan inte göra:

 this.someMember();

När du öppnar medlemsfunktioner eller medlemsvariabler från pekare med hjälp av pilsymbolen -> :

this->someMember();

Andra användbara länkar till bättre förståelse av den this pekaren:

Vad är "den här" pekaren?

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

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

den här pekaren

Alla icke-statiska medlemsfunktioner har en dold parameter, en pekare till en instans av klassen, med namnet this ; denna parameter infogas tyst i början av parameterlistan och hanteras helt av kompilatorn. När en medlem av klassen nås inuti en medlemsfunktion åtgår den tyst genom this ; detta gör det möjligt för kompilatorn att använda en enda icke-statisk medlemsfunktion i alla fall, och tillåter en medlemsfunktion att anropa andra medlemsfunktioner polymorf.

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

I en konstruktör kan this säkert användas för (implicit eller uttryckligen) åtkomst till alla fält som redan har initierats, eller något fält i en överordnad klass; omvänt, (implicit eller uttryckligen) åtkomst till fält som ännu inte har initialiserats, eller några fält i en härledd klass, är osäkra (på grund av att den härledda klassen ännu inte har konstruerats, och därför är dess fält varken initialiserade eller befintliga). Det är också osäkert att anropa virtuella medlemsfunktioner genom this i konstruktören, eftersom alla härledda klassfunktioner inte kommer att beaktas (på grund av att den härledda klassen ännu inte har konstruerats, och därmed dess konstruktör ännu inte uppdaterar vtabellen).


Observera också att medan i en konstruktör är objektets typ den typ som konstruktören konstruerar. Detta gäller även om objektet deklareras som en härledd typ. I exemplet nedan är till exempel ctd_good och ctd_bad typ CtorThisBase inuti CtorThisBase() och typ CtorThis in CtorThis() , även om deras kanoniska typ är CtorThisDerived . När de mer härledda klasserna är konstruerade runt basklassen, går instansen gradvis genom klasshierarkin tills den är en helt konstruerad instans av dess avsedda typ.

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

Med dessa klasser och medlemsfunktioner:

  • I den goda konstruktören, för ctd_good :
    • CtorThisBase är helt konstruerad när CtorThis konstruktorn anges. Därför är s i ett giltigt tillstånd under initialisering av i , och kan således nås.
    • i initialiseras innan j(this->i) nås. Därför är i i ett giltigt tillstånd när i initierar j , och kan således nås.
    • j initialiseras innan k(j) nås. Därför är j i ett giltigt tillstånd under initialisering av k , och kan således nås.
  • I den dåliga konstruktören, för ctd_bad :
    • k initialiseras efter att j(this->k) har nåtts. Därför är k i ogiltigt tillstånd under initialisering av j , och åtkomst till det orsakar odefinierat beteende.
    • CtorThisDerived konstrueras inte förrän efter att CtorThis har konstruerats. Därför är b i ogiltigt tillstånd under initialisering av k , och åtkomst till det orsakar odefinierat beteende.
    • Objektet ctd_bad är fortfarande en CtorThis tills det lämnar CtorThis() och kommer inte att uppdateras för att använda CtorThisDerived vtable förrän CtorThisDerived() . Därför kommer virt_func() att ringa CtorThis::virt_func() , oavsett om det är avsett att ringa det eller CtorThisDerived::virt_func() .

Använd den här pekaren för att få åtkomst till medlemsdata

I detta sammanhang är det inte helt nödvändigt att använda this pekare, men det kommer att göra din kod tydligare för läsaren genom att indikera att en given funktion eller variabel är medlem i klassen. Ett exempel i denna situation:

// 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;
}

Se det i aktion här .

Använd denna pekare för att skilja mellan medlemsdata och parametrar

Detta är en verklig användbar strategi för att skilja medlemsdata från parametrar ... Låt oss ta detta exempel:

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

Du kan se här i konstruktören vi utför följande:

this->name = name; 

Här kan du se att vi fastställer parameternamnet till namnet på den privata variabeln från klassen Dog (detta-> namn).

För att se utgången från koden ovan: http://cpp.sh/75r7

denna Pointer CV-kval

this kan också vara cv-kvalificerat, samma som alla andra pekare. På grund av att this parameter inte listas i parameterlistan krävs dock speciell syntax för detta; cv-kvalificeringarna listas efter parameterlistan, men före funktionens kropp.

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*
};

Som this är en parameter, en kan funktion överbelastas baserat på dess this cv-kval (er) .

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

När this är const (inklusive const volatile ) kan funktionen inte skriva till medlemsvariabler genom det, vare sig det implicit eller uttryckligen. Det enda undantaget från detta är mutable medlemsvariabler , som kan skrivas oavsett konstens. På grund av detta används const för att indikera att medlemsfunktionen inte ändrar objektets logiska tillstånd (hur objektet verkar för omvärlden), även om det ändrar det fysiska tillståndet (hur objektet ser ut under huven ).

Logiskt tillstånd är det sätt som objektet verkar för utanför observatörer. Det är inte direkt bundet till fysiskt tillstånd, och kanske inte ens förvaras som fysiskt tillstånd. Så länge utanför observatörer inte kan se några förändringar, är det logiska tillståndet konstant, även om du vänder varje bit i objektet.

Fysiskt tillstånd, även känt som bitvis tillstånd, är hur objektet lagras i minnet. Detta är objektets snygga, de råa 1s och 0s som utgör dess data. Ett objekt är endast fysiskt konstant om dess representation i minnet aldrig förändras.

Notera att C ++ baser const ness på logiskt tillstånd, inte fysiska tillstånd.

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

Observera att även om du tekniskt skulle kunna använda const_castthis att göra det icke-cv-kvalificerat, så skulle du verkligen, VERKLIGT inte, och bör använda mutable istället. En const_cast kan påkalla odefinierat beteende när det används på ett objekt som faktiskt är const , medan mutable är utformat för att vara säkert att använda. Det är dock möjligt att du kan stöta på detta i extremt gammal kod.

Ett undantag från denna regel är att definiera icke-cv-kvalificerade accessorer när det gäller const accessors; eftersom objektet garanteras att inte vara const om den icke-cv-kvalificerade versionen heter, finns det ingen risk för 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));
    }
};

Detta förhindrar onödig duplikering av kod.


Liksom med vanliga pekare, om this är volatile (inklusive const volatile ), laddas det från minnet varje gång det går åt, istället för att cache. Detta har samma effekter på optimeringen som att deklarerar att någon annan pekare skulle vara volatile , så man bör vara försiktig.


Notera att om en instans är CV-kvalificerade, det enda medlemsfunktioner är det tillåtet att komma åt är medlemsfunktioner vars this pekare är minst lika cv kvalificerade som förekomst själv:

  • Icke-cv-instanser kan komma åt alla medlemsfunktioner.
  • const instanser kan komma åt const och const volatile funktioner.
  • volatile instanser får åtkomst till volatile och const volatile funktioner.
  • const volatile instanser kan komma åt const volatile funktioner.

Detta är en av de viktigaste principerna för const correctness .

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.

denna Pointer Ref-Qualifiers

C ++ 11

På samma sätt som this cv-kval kan vi också tillämpa ref-kval för *this . Ref-qualifiers används för att välja mellan normal och rvalue referens semantik, vilket gör att kompilatorn kan använda antingen kopiera eller flytta semantik beroende på vilka som är mer lämpliga och tillämpas på *this istället för this .

Observera att trots ref-kvalificeringar som använder referenssyntax, är this fortfarande en pekare. Observera också att ref-kvalificeringarna faktiskt inte ändrar typen av *this ; det är bara lättare att beskriva och förstå deras effekter genom att titta på dem som om de gjorde.

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

En medlemsfunktion kan inte ha överbelastningar både med och utan ref-kval. programmeraren måste välja mellan det ena eller det andra. Tack och lov kan cv-kvalificeringarna användas i samband med ref-kvalificeringarna, så att reglerna för const korrekthet kan följas.

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
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow