C++
De deze aanwijzer
Zoeken…
Opmerkingen
De this
aanwijzer is een sleutelwoord voor C ++ en daarom is er geen bibliotheek nodig om dit te implementeren. En vergeet niet dat this
een wijzer is! Dus je kunt niet doen:
this.someMember();
Wanneer u lidfuncties of lidvariabelen opent vanuit aanwijzers met het pijlsymbool ->
:
this->someMember();
Andere nuttige links naar een beter begrip van this
aanwijzer:
http://www.geeksforgeeks.org/this-pointer-in-c/
https://www.tutorialspoint.com/cplusplus/cpp_this_pointer.htm
deze aanwijzer
Alle niet-statische lidfuncties hebben een verborgen parameter, een verwijzing naar een instantie van de klasse, this
genaamd; deze parameter wordt stil toegevoegd aan het begin van de parameterlijst en wordt volledig afgehandeld door de compiler. Wanneer een lid van de klas binnen een ledenfunctie wordt benaderd, wordt het stilzwijgend bereikt via this
; Hierdoor kan de compiler een enkele niet-statische lidfunctie gebruiken voor alle instanties en kan een lidfunctie andere lidfuncties polymorf aanroepen.
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; }
In een constructor kan this
veilig worden gebruikt om (impliciet of expliciet) toegang te krijgen tot elk veld dat al is geïnitialiseerd, of elk veld in een bovenliggende klasse; omgekeerd is (impliciet of expliciet) toegang tot velden die nog niet zijn geïnitialiseerd, of velden in een afgeleide klasse, onveilig (omdat de afgeleide klasse nog niet is geconstrueerd, en dus zijn velden niet geïnitialiseerd of bestaand). Het is ook onveilig om via this
functie virtuele lidfuncties in de constructor aan te roepen, omdat afgeleide klassefuncties niet in aanmerking worden genomen (vanwege de afgeleide klasse die nog niet is geconstrueerd en dus de constructor de vtable nog niet bijwerkt).
Merk ook op dat terwijl in een constructor, het type van het object het type is dat die constructor construeert. Dit geldt zelfs als het object als een afgeleid type wordt gedeclareerd. In het onderstaande voorbeeld zijn bijvoorbeeld ctd_good
en ctd_bad
type CtorThisBase
in CtorThisBase()
en type CtorThis
in CtorThis()
, ook al is hun canonieke type CtorThisDerived
. Omdat de meer afgeleide klassen zijn opgebouwd rond de basisklasse, gaat de instantie geleidelijk door de klassenhiërarchie totdat deze een volledig geconstrueerde instantie van het beoogde type is.
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);
Met deze klassen en ledenfuncties:
- In de goede constructor, voor
ctd_good
:-
CtorThisBase
is volledig gebouwd tegen de tijd dat deCtorThis
constructor wordt ingevoerd. Daarom bevindts
zich tijdens het initialiseren vani
in een geldige status en is dus toegankelijk. -
i
wordt geïnitialiseerd voordatj(this->i)
wordt bereikt. Daarom bevindti
zich in een geldige status tijdens het initialiseren vanj
en is dus toegankelijk. -
j
wordt geïnitialiseerd voordatk(j)
wordt bereikt. Daarom bevindtj
zich in een geldige status tijdens het initialiseren vank
en is dus toegankelijk.
-
- In de slechte constructor, voor
ctd_bad
:-
k
wordt geïnitialiseerd nadatj(this->k)
is bereikt. Daarom bevindtk
zich in een ongeldige status tijdens het initialiseren vanj
en veroorzaakt toegang tot ongedefinieerd gedrag. -
CtorThisDerived
wordt pas gebouwd nadatCtorThis
is gebouwd. Daarom bevindtb
zich in een ongeldige status tijdens het initialiseren vank
en veroorzaakt toegang tot ongedefinieerd gedrag. - Het object
ctd_bad
is nog steeds eenCtorThis
totdat hetCtorThis()
verlaat en zal niet worden bijgewerkt om deCtorThisDerived
van CtorThisDerived te gebruiken totCtorThisDerived()
. Daarom zalvirt_func()
CtorThis::virt_func()
, ongeacht of het de bedoeling is dat aan te roepen ofCtorThisDerived::virt_func()
.
-
Gebruik deze aanwijzer om toegang te krijgen tot lidgegevens
In deze context is het gebruik van this
aanwijzer niet helemaal noodzakelijk, maar het zal uw code duidelijker maken voor de lezer, door aan te geven dat een bepaalde functie of variabele lid is van de klasse. Een voorbeeld in deze situatie:
// 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;
}
Zie het in actie hier .
Gebruik deze aanwijzer om onderscheid te maken tussen lidgegevens en parameters
Dit is een echte nuttige strategie om lidgegevens te onderscheiden van parameters ... Laten we dit voorbeeld nemen:
// 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();
}
U kunt hier in de constructor zien dat we het volgende uitvoeren:
this->name = name;
Hier kunt u zien dat we de parameternaam vergelijken met de naam van de privévariabele uit de klasse Dog (deze-> naam).
Om de uitvoer van bovenstaande code te zien: http://cpp.sh/75r7
deze Pointer CV-kwalificaties
this
kan ook cv-gekwalificeerd zijn, hetzelfde als elke andere aanwijzer. Omdat this
parameter niet in de parameterlijst wordt vermeld, is hiervoor een speciale syntaxis vereist; de cv-kwalificaties worden vermeld na de parameterlijst, maar vóór het hoofdgedeelte van de functie.
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*
};
Omdat this
een parameter is, kan een functie worden overbelast op basis van this
cv-kwalificatie (s) .
struct CVOverload {
int func() { return 3; }
int func() const { return 33; }
int func() volatile { return 333; }
int func() const volatile { return 3333; }
};
Als this
const
(inclusief const volatile
), kan de functie er niet impliciet of expliciet naar lidvariabelen naar schrijven. De enige uitzondering hierop zijn mutable
lidvariabelen , die kunnen worden geschreven ongeacht het feit. Hierdoor wordt const
gebruikt om aan te geven dat de lidfunctie de logische status van het object (de manier waarop het object naar de buitenwereld verschijnt) niet verandert, zelfs als het de fysieke status wijzigt (de manier waarop het object onder de motorkap kijkt) ).
Logische status is de manier waarop het object wordt weergegeven aan externe waarnemers. Het is niet direct gekoppeld aan fysieke toestand en kan zelfs niet eens als fysieke toestand worden opgeslagen. Zolang waarnemers van buitenaf geen wijzigingen kunnen zien, is de logische status constant, zelfs als u alle bits in het object omdraait.
Fysieke toestand, ook bekend als bitgewijze toestand, is hoe het object in het geheugen wordt opgeslagen. Dit is de nitty-gritty van het object, de ruwe enen en nullen waaruit de gegevens bestaan. Een object is alleen fysiek constant als de weergave ervan in het geheugen nooit verandert.
Merk op dat C ++ const
baseert op logische toestand, niet op fysieke toestand.
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;
}
Merk op dat terwijl je technisch zou kunnen gebruiken const_cast
op this
om het non-cv-gekwalificeerd te maken, je echt, echt niet, en moet gebruiken mutable
in plaats daarvan. Een const_cast
dreigt te roepen onbepaald gedrag bij gebruik op een object dat eigenlijk const
, terwijl mutable
is bedoeld veilig te gebruiken zijn. Het is echter mogelijk dat u dit tegenkomt in extreem oude code.
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));
}
};
Dit voorkomt onnodige duplicatie van code.
Net als bij reguliere pointers, wordt this
, als this
volatile
(inclusief const volatile
), elke keer dat het wordt gebruikt uit het geheugen geladen, in plaats van in de cache te worden opgeslagen. Dit heeft dezelfde effecten op de optimalisatie als een andere aanwijzer volatile
zou verklaren, dus wees voorzichtig.
Merk op dat als een instantie cv-gekwalificeerd is, de enige lidfuncties waartoe het toegang heeft, lidfuncties zijn waarvan this
aanwijzer minstens even cv-gekwalificeerd is als de instantie zelf:
- Niet-cv-instanties hebben toegang tot alle lidfuncties.
-
const
instanties hebben toegang totconst
enconst volatile
functies. -
volatile
instanties hebben toegang totvolatile
enconst volatile
functies. -
const volatile
instanties hebben toegang totconst volatile
functies.
Dit is een van de belangrijkste principes van const
correctheid .
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.
deze Pointer Ref-kwalificaties
Net als bij this
cv-kwalificaties kunnen we hier ook *this
ref-kwalificaties op toepassen. Ref-kwalificaties worden gebruikt om te kiezen tussen normale en rvalue-referentiesemantiek, waardoor de compiler semantiek kan kopiëren of verplaatsen, afhankelijk van welke meer geschikt zijn, en op *this
plaats this
worden toegepast.
Merk op dat ondanks ref-kwalificaties die syntaxis van referenties gebruiken, this
zelf nog steeds een pointer is. Merk ook op dat ref-qualifiers het type *this
niet veranderen; het is gewoon gemakkelijker om hun effecten te beschrijven en te begrijpen door ernaar te kijken alsof ze dat deden.
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
Een ledenfunctie kan geen overbelasting hebben, zowel met als zonder ref-kwalificaties; de programmeur moet kiezen tussen het een of het ander. Gelukkig kunnen cv-qualifiers worden gebruikt in combinatie met ref-qualifiers, waardoor const
correctheidsregels kunnen worden gevolgd.
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&& {}
};