C++
Den här pekaren
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:
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ärCtorThis
konstruktorn anges. Därför ärs
i ett giltigt tillstånd under initialisering avi
, och kan således nås. -
i
initialiseras innanj(this->i)
nås. Därför äri
i ett giltigt tillstånd näri
initierarj
, och kan således nås. -
j
initialiseras innank(j)
nås. Därför ärj
i ett giltigt tillstånd under initialisering avk
, och kan således nås.
-
- I den dåliga konstruktören, för
ctd_bad
:-
k
initialiseras efter attj(this->k)
har nåtts. Därför ärk
i ogiltigt tillstånd under initialisering avj
, och åtkomst till det orsakar odefinierat beteende. -
CtorThisDerived
konstrueras inte förrän efter attCtorThis
har konstruerats. Därför ärb
i ogiltigt tillstånd under initialisering avk
, och åtkomst till det orsakar odefinierat beteende. - Objektet
ctd_bad
är fortfarande enCtorThis
tills det lämnarCtorThis()
och kommer inte att uppdateras för att användaCtorThisDerived
vtable förränCtorThisDerived()
. Därför kommervirt_func()
att ringaCtorThis::virt_func()
, oavsett om det är avsett att ringa det ellerCtorThisDerived::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_cast
på this
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.
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 åtconst
ochconst volatile
funktioner. -
volatile
instanser får åtkomst tillvolatile
ochconst volatile
funktioner. -
const volatile
instanser kan komma åtconst 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
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&& {}
};