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:-
CtorThisBaseis volledig gebouwd tegen de tijd dat deCtorThisconstructor wordt ingevoerd. Daarom bevindtszich tijdens het initialiseren vaniin een geldige status en is dus toegankelijk. -
iwordt geïnitialiseerd voordatj(this->i)wordt bereikt. Daarom bevindtizich in een geldige status tijdens het initialiseren vanjen is dus toegankelijk. -
jwordt geïnitialiseerd voordatk(j)wordt bereikt. Daarom bevindtjzich in een geldige status tijdens het initialiseren vanken is dus toegankelijk.
-
- In de slechte constructor, voor
ctd_bad:-
kwordt geïnitialiseerd nadatj(this->k)is bereikt. Daarom bevindtkzich in een ongeldige status tijdens het initialiseren vanjen veroorzaakt toegang tot ongedefinieerd gedrag. -
CtorThisDerivedwordt pas gebouwd nadatCtorThisis gebouwd. Daarom bevindtbzich in een ongeldige status tijdens het initialiseren vanken veroorzaakt toegang tot ongedefinieerd gedrag. - Het object
ctd_badis nog steeds eenCtorThistotdat hetCtorThis()verlaat en zal niet worden bijgewerkt om deCtorThisDerivedvan 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.
-
constinstanties hebben toegang totconstenconst volatilefuncties. -
volatileinstanties hebben toegang totvolatileenconst volatilefuncties. -
const volatileinstanties hebben toegang totconst volatilefuncties.
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&& {}
};