C++
Этот указатель
Поиск…
замечания
this
указатель является ключевым словом C ++ Therfore нет библиотеки , необходимой для реализации этого. И не забывайте, что this
указатель! Таким образом, вы не можете:
this.someMember();
Когда вы обращаетесь к элементам-членам или переменным-членам из указателей, используя символ стрелки ->
:
this->someMember();
Другие полезные ссылки на лучшее понимание this
указателя:
http://www.geeksforgeeks.org/this-pointer-in-c/
https://www.tutorialspoint.com/cplusplus/cpp_this_pointer.htm
этот указатель
Все нестатические функции-члены имеют скрытый параметр, указатель на экземпляр класса, названный this
; этот параметр тихо вставлен в начале списка параметров и полностью обрабатывается компилятором. Когда член класса доступен внутри функции члена, она молча доступ через this
; это позволяет компилятору использовать одну нестационарную функцию-член для всех экземпляров и позволяет функции-члене полиморфно вызывать другие функции-члены.
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; }
В конструкторе this
можно безопасно использовать (неявно или явно) для доступа к любому уже инициализированному полю или к любому полю родительского класса; наоборот, (неявно или явно) доступ к любым полям, которые еще не были инициализированы или какие-либо поля в производном классе, является небезопасным (из-за того, что производный класс еще не сконструирован, и, следовательно, его поля не инициализируются и не существуют). Также небезопасно вызывать функции виртуального члена через this
в конструкторе, так как любые производные функции класса не рассматриваются (из-за того, что производный класс еще не сконструирован, и, следовательно, его конструктор еще не обновил vtable).
Также обратите внимание, что, хотя в конструкторе тип объекта является типом, который строится конструктором. Это справедливо, даже если объект объявлен как производный тип. Например, в приведенном ниже примере ctd_good
и ctd_bad
являются типами CtorThisBase
внутри CtorThisBase()
и набирают CtorThis
внутри CtorThis()
, хотя их канонический тип - CtorThisDerived
. Поскольку более производные классы построены вокруг базового класса, экземпляр постепенно переходит через иерархию классов до тех пор, пока он не будет полностью сконструированным экземпляром его предполагаемого типа.
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);
С этими классами и функциями-членами:
- В хорошем конструкторе для
ctd_good
:-
CtorThisBase
полностью сконструирован к моментуCtorThis
конструктораCtorThis
. Следовательно,s
находится в допустимом состоянии при инициализацииi
, и поэтому к нему можно получить доступ. -
i
инициализируется до достиженияj(this->i)
. Следовательно,i
находится в допустимом состоянии при инициализацииj
, и поэтому к нему можно получить доступ. -
j
инициализируется до достиженияk(j)
. Следовательно,j
находится в допустимом состоянии при инициализацииk
и, таким образом, можно получить доступ.
-
- В плохом конструкторе для
ctd_bad
:-
k
инициализируется после достиженияj(this->k)
. Следовательно,k
находится в недопустимом состоянии при инициализацииj
, и доступ к нему вызывает неопределенное поведение. -
CtorThisDerived
неCtorThisDerived
до тех пор, покаCtorThis
будет построен. Следовательно,b
находится в недопустимом состоянии при инициализацииk
, и доступ к нему вызывает неопределенное поведение. - Объект
ctd_bad
ещеCtorThis
, пока он не покинетCtorThis()
, и не будет обновляться, чтобы использоватьCtorThisDerived
виртуальные таблицы «s доCtorThisDerived()
. Следовательно,virt_func()
вызоветCtorThis::virt_func()
, независимо от того, предназначено ли это для вызова илиCtorThisDerived::virt_func()
.
-
Использование этого указателя для доступа к данным участника
В этом контексте использование this
указателя не является абсолютно необходимым, но это сделает ваш код более понятным для читателя, указав, что данная функция или переменная является членом класса. Пример в этой ситуации:
// 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;
}
Смотрите в действии здесь .
Использование этого указателя для дифференциации данных и параметров элемента
Это фактическая полезная стратегия для дифференциации данных членов из параметров ... Давайте рассмотрим этот пример:
// 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();
}
Вы можете видеть здесь, в конструкторе мы выполняем следующее:
this->name = name;
Здесь вы можете увидеть, что имя параметра присваивается имени частной переменной из класса Dog (this-> name).
Чтобы увидеть вывод вышеуказанного кода: http://cpp.sh/75r7
этот указатель CV-Qualifiers
this
также может быть cv-квалифицированным, как и любой другой указатель. Однако из-за того, что this
параметр не указан в списке параметров, для этого требуется специальный синтаксис; cv-квалификаторы перечисляются после списка параметров, но перед телом функции.
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*
};
Поскольку this
параметр, функция может быть перегружена на основе this
cv-квалификатора (ов) .
struct CVOverload {
int func() { return 3; }
int func() const { return 33; }
int func() volatile { return 333; }
int func() const volatile { return 3333; }
};
Когда this
const
(в том числе const volatile
), функция не может записывать в него переменные-члены, неявно или явно. Единственным исключением из этого является mutable
переменные-члены , которые могут быть записаны независимо от константы. Из-за этого const
используется, чтобы указать, что функция-член не изменяет логическое состояние объекта (способ, которым объект появляется во внешнем мире), даже если он изменяет физическое состояние (способ, которым объект выглядит под капотом ).
Логическое состояние - это способ, которым объект кажется внешним наблюдателям. Он напрямую не привязан к физическому состоянию и даже не может быть сохранен как физическое состояние. Пока внешние наблюдатели не могут видеть никаких изменений, логическое состояние является постоянным, даже если вы переворачиваете каждый бит в объекте.
Физическое состояние, также известное как побитовое состояние, заключается в том, как объект хранится в памяти. Это объектно-мелочные, необработанные 1s и 0s, которые составляют его данные. Объект является только физически постоянным, если его представление в памяти никогда не изменяется.
Следует отметить , что C ++ основы const
Несс на логическом состоянии, а не физическое состояние.
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;
}
Обратите внимание, что, хотя вы технически можете использовать const_cast
для this
чтобы сделать его не-cv-квалифицированным, вы действительно, ДЕЙСТВИТЕЛЬНО не должны, и вместо этого должны использовать mutable
. const_cast
может вызывать неопределенное поведение при использовании объекта, который фактически является const
, в то время как mutable
предназначен для безопасного использования. Однако, возможно, вы можете столкнуться с этим в чрезвычайно старом коде.
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));
}
};
Это предотвращает ненужное дублирование кода.
Как и в случае с обычными указателями, если this
является volatile
(в том числе const volatile
), он загружается из памяти каждый раз, когда к нему обращаются, вместо кэширования. Это оказывает такое же влияние на оптимизацию, как и объявление любого другого указателя volatile
, поэтому следует проявлять осторожность.
Обратите внимание, что если экземпляр имеет cv-квалификацию, единственными функциями-членами, которым разрешен доступ, являются функции-члены, у которых this
указатель, по меньшей мере, cv-квалифицирован как сам экземпляр:
- Не-cv-экземпляры могут обращаться к любым функциям-членам.
-
const
могут обращаться кconst
иconst volatile
. -
volatile
экземпляры могут обращаться кvolatile
иconst volatile
функциям. -
const volatile
могут обращаться кconst volatile
функциям.
Это один из ключевых принципов корректности const
.
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.
этот указатель Ref-Qualifiers
Подобно this
cv-квалификаторам, мы также можем применить ref-qualifiers к *this
. Ref-qualifiers используются для выбора между семантикой нормального и rvalue-ссылок, позволяющей компилятору использовать либо семантику копирования или перемещения, в зависимости от того, что является более подходящим, и применяется к *this
вместо this
.
Обратите внимание, что, несмотря на ref-qualifiers с использованием ссылочного синтаксиса, this
само по-прежнему является указателем. Также обратите внимание, что ref-qualifiers фактически не изменяют тип *this
; это просто легче описать и понять их последствия, глядя на них, как будто они это сделали.
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
Функция-член не может иметь перегрузок как с реф-квалификаторами, так и без них; программист должен выбирать один или другой. К счастью, CV-классификаторы могут быть использованы в сочетании с реф-классификаторов, что позволяет const
корректности правила , которым необходимо следовать.
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&& {}
};