Szukaj…


Uwagi

this wskaźnik jest kluczowe dla C ++ therfore nie jest potrzebne do wdrożenia tej biblioteki. I nie zapominaj, this to wskaźnik! Więc nie możesz zrobić:

 this.someMember();

Podczas uzyskiwania dostępu do funkcji składowych lub zmiennych składowych ze wskaźników za pomocą symbolu strzałki -> :

this->someMember();

Inne pomocne linki do lepszego zrozumienia this wskaźnika:

Co to jest wskaźnik „ten”?

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

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

ten wskaźnik

Wszystkie niestatyczne funkcje składowe mają ukryty parametr, wskaźnik do instancji klasy, o nazwie this ; ten parametr jest cicho wstawiany na początku listy parametrów i obsługiwany w całości przez kompilator. Gdy dostęp do członka klasy jest uzyskiwany w ramach funkcji członka, jest on uzyskiwany w this po cichu; pozwala to kompilatorowi na użycie jednej niestatycznej funkcji składowej dla wszystkich instancji i pozwala funkcji wywoływać polimorficznie inne funkcje składowe.

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

W konstruktorze można this bezpiecznie wykorzystać (domyślnie lub jawnie) do uzyskania dostępu do dowolnego pola, które zostało już zainicjowane, lub dowolnego pola w klasie nadrzędnej; i odwrotnie (dostęp niejawny lub jawny) do pól, które nie zostały jeszcze zainicjowane, lub dowolnych pól w klasie pochodnej, jest niebezpieczny (ze względu na to, że klasa pochodna nie została jeszcze zbudowana, a zatem jej pola nie zostały zainicjowane ani nie istnieją). Nie jest również niebezpieczne wywoływanie wirtualnych funkcji składowych przez this w konstruktorze, ponieważ żadne pochodne funkcje klasowe nie będą brane pod uwagę (ze względu na to, że klasa pochodna nie jest jeszcze budowana, a zatem jej konstruktor nie aktualizuje jeszcze vtable).


Zauważ też, że będąc w konstruktorze, typem obiektu jest typ, który ten konstruktor konstruuje. Dzieje się tak nawet wtedy, gdy obiekt jest zadeklarowany jako typ pochodny. Na przykład w poniższym przykładzie ctd_good i ctd_bad są typu CtorThisBase wewnątrz CtorThisBase() , a typ CtorThis wewnątrz CtorThis() , nawet jeśli ich typem kanonicznym jest CtorThisDerived . Ponieważ klasy bardziej pochodne są konstruowane wokół klasy podstawowej, instancja stopniowo przechodzi przez hierarchię klas, aż staje się w pełni skonstruowaną instancją zgodną z przeznaczeniem.

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

Dzięki tym klasom i funkcjom członkowskim:

  • W dobrym konstruktorze dla ctd_good :
    • CtorThisBase jest w pełni zbudowany do czasu wprowadzenia konstruktora CtorThis . Dlatego s jest w prawidłowym stanie podczas inicjowania i , i dlatego można uzyskać do niego dostęp.
    • i jest inicjowany przed osiągnięciem j(this->i) . Dlatego i jest w prawidłowym stanie podczas inicjowania j , i dlatego można uzyskać do niego dostęp.
    • j jest inicjalizowane przed osiągnięciem k(j) . Dlatego j jest w prawidłowym stanie podczas inicjowania k , a zatem można uzyskać do niego dostęp.
  • W złym konstruktorze dla ctd_bad :
    • k jest inicjalizowane po osiągnięciu j(this->k) . Dlatego k jest w niepoprawnym stanie podczas inicjowania j , a dostęp do niego powoduje niezdefiniowane zachowanie.
    • CtorThisDerived jest budowany dopiero po CtorThis . Dlatego b jest w nieprawidłowym stanie podczas inicjowania k , a dostęp do niego powoduje niezdefiniowane zachowanie.
    • Przedmiotem ctd_bad jest wciąż CtorThis aż pozostawia CtorThis() , a nie zostaną zaktualizowane do korzystania CtorThisDerived vtable „s aż CtorThisDerived() . Dlatego virt_func() wywoła CtorThis::virt_func() , niezależnie od tego, czy ma to wywoływać, czy CtorThisDerived::virt_func() .

Korzystanie z tego wskaźnika w celu uzyskania dostępu do danych członka

W tym kontekście użycie this wskaźnika nie jest całkowicie konieczne, ale sprawi, że twój kod będzie bardziej czytelny, wskazując, że dana funkcja lub zmienna należy do klasy. Przykład w tej sytuacji:

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

Zobacz to w akcji tutaj .

Używanie tego wskaźnika do rozróżnienia danych elementu i parametrów

To jest rzeczywiście przydatna strategia odróżnienia danych członka od parametrów ... Weźmy ten przykład:

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

Tutaj możesz zobaczyć w konstruktorze, że wykonujemy następujące czynności:

this->name = name; 

Tutaj widać, że przypisujemy nazwę parametru do nazwy zmiennej prywatnej z klasy Dog (this-> name).

Aby zobaczyć wynik powyższego kodu: http://cpp.sh/75r7

ten wskaźnik kwalifikatory CV

this może być również cv wykwalifikowany, tak samo jak każdy inny wskaźnik. Jednak ze względu na brak this parametru na liście parametrów wymagana jest do tego specjalna składnia; kwalifikatory cv są wymienione po liście parametrów, ale przed treścią funkcji.

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

Jak this jest parametrów, funkcja może być przeciążony w oparciu o this cv-kwalifikacjach (ów) .

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

Kiedy this jest const (w tym const volatile ), funkcja nie jest w stanie napisać do zmiennych składowych przez nią, czy jawnie lub niejawnie. Jedynym wyjątkiem są mutable zmienne mutable , które można zapisać niezależnie od ciągłości. Z tego powodu const służy do wskazania, że funkcja członka nie zmienia stanu logicznego obiektu (sposób, w jaki obiekt wygląda na świat zewnętrzny), nawet jeśli zmienia stan fizyczny (sposób, w jaki obiekt wygląda pod maską) ).

Stan logiczny to sposób, w jaki obiekt wydaje się obserwatorom zewnętrznym. Nie jest bezpośrednio związany ze stanem fizycznym, a nawet może nie być przechowywany jako stan fizyczny. Dopóki zewnętrzni obserwatorzy nie widzą żadnych zmian, stan logiczny jest stały, nawet jeśli odwrócisz każdy bit w obiekcie.

Stan fizyczny, znany również jako stan bitowy, to sposób przechowywania obiektu w pamięci. To jest drobiazgowość obiektu, surowe 1 i 0, które składają się na jego dane. Obiekt jest fizycznie stały tylko wtedy, gdy jego reprezentacja w pamięci nigdy się nie zmienia.

Należy pamiętać, że C ++ podstawy const ności od stanu logicznego, a nie stanu fizycznego.

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

Należy pamiętać, że podczas technicznie przydałby const_cast na this , aby go non-cv-wykwalifikowanych, naprawdę, naprawdę nie powinno, i powinna wykorzystać mutable zamiast. const_cast może wywoływać niezdefiniowane zachowanie, gdy jest używany na obiekcie, który faktycznie jest const , podczas gdy mutable jest zaprojektowana tak, aby była bezpieczna w użyciu. Możliwe jest jednak, że natrafisz na to w bardzo starym kodzie.

Wyjątkiem od tej reguły jest definiowanie akcesorów niekwalifikowanych do CV pod względem akcesorów const ; ponieważ obiekt ma gwarancję, że nie będzie const jeśli zostanie wywołana wersja niekwalifikowana do CV, nie ma ryzyka 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));
    }
};

Zapobiega to niepotrzebnemu powielaniu kodu.


Podobnie jak w przypadku zwykłych wskazówek, jeśli this jest volatile (w tym const volatile ), to jest ładowany z pamięci za każdym razem, gdy jest dostępne, zamiast być buforowane. Ma to taki sam wpływ na optymalizację, jak deklarowanie jakiegokolwiek innego wskaźnika volatile , dlatego należy zachować ostrożność.


Zauważ, że jeśli instancja jest zakwalifikowana do cv, jedynymi funkcjami składowymi, do których ma dostęp, są funkcje składowe, których this wskaźnik jest co najmniej tak samo zakwalifikowany do cv, jak sama instancja:

  • Instancje inne niż cv mogą uzyskać dostęp do dowolnych funkcji członkowskich.
  • Instancje const mają dostęp do funkcji const i const volatile .
  • wystąpienia volatile mogą uzyskać dostęp do funkcji volatile i const volatile .
  • const volatile instancje mogą uzyskać dostęp do const volatile funkcji.

Jest to jedna z kluczowych zasad const poprawności .

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.

ten wskaźnik Ref-Qualifiers

C ++ 11

Podobnie jak w przypadku this kwalifikatorów cv, możemy również zastosować *this kwalifikatory ref . Kwalifikatory odniesienia służą do wyboru semantyki odniesienia normalnej i wartości rvalue, umożliwiając kompilatorowi użycie semantyki kopiowania lub przenoszenia w zależności od tego, które są bardziej odpowiednie, i są stosowane do *this zamiast this .

Zauważ, że pomimo kwalifikatorów odniesienia korzystających ze składni referencyjnej, this samo w sobie jest wskaźnikiem. Zauważ też, że kwalifikatory ref nie zmieniają typu *this ; po prostu łatwiej jest opisać i zrozumieć ich skutki, patrząc na nich tak, jakby to zrobili.

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

Funkcja członka nie może mieć przeciążeń zarówno z kwalifikatorami referencyjnymi, jak i bez nich; programista musi wybierać między jednym a drugim. Na szczęście kwalifikatory cv mogą być używane w połączeniu z kwalifikatorami referencyjnymi, umożliwiając przestrzeganie reguł const poprawności .

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
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow