Поиск…


замечания

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 предназначен для безопасного использования. Однако, возможно, вы можете столкнуться с этим в чрезвычайно старом коде.

Исключением из этого правила является определение не-cv-квалифицированных аксессоров в терминах const устройств; поскольку объект гарантированно не будет const если вызывается не-cv-квалифицированная версия, нет никакого риска 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));
    }
};

Это предотвращает ненужное дублирование кода.


Как и в случае с обычными указателями, если 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

C ++ 11

Подобно 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&& {}
};


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow