Horje

C++ - C++ | The This Pointer
Start
  The This Pointer
  Using the this Pointer to Access Member Data

In this context, using the this pointer isn't entirely necessary, but it will make your code clearer to the reader, by indicating that a given function or variable is a member of the class. An example in this 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;
}

See it in action here (opens new window).

  Using the this Pointer to Differentiate Between Member Data and Parameters

This is an actual useful strategy to differentiate member data from parameters... Lets take this example :

// 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();
}

You can see here in the constructor we execute the following:

this->name = name; 

Here , you can see we are assinging the parameter name to the name of the private variable from the class Dog(this->name) .

To see the output of above code : http://cpp.sh/75r7 (opens new window)

  this Pointer CV-Qualifiers

this can also be cv-qualified, the same as any other pointer. However, due to the this parameter not being listed in the parameter list, special syntax is required for this; the cv-qualifiers are listed after the parameter list, but before the function's body.

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*
};

As this is a parameter, a function can be overloaded based on its this cv-qualifier(s) (opens new window).

struct CVOverload {
    int func()                { return    3; }
    int func() const          { return   33; }
    int func() volatile       { return  333; }
    int func() const volatile { return 3333; }
};

When this is const (including const volatile), the function is unable to write to member variables through it, whether implicitly or explicitly. The sole exception to this is mutable member variables (opens new window), which can be written regardless of const-ness. Due to this, const is used to indicate that the member function doesn't change the object's logical state (the way the object appears to the outside world), even if it does modify the physical state (the way the object looks under the hood).

Logical state is the way the object appears to outside observers. It isn't directly tied to physical state, and indeed, might not even be stored as physical state. As long as outside observers can't see any changes, the logical state is constant, even if you flip every single bit in the object.

Physical state, also known as bitwise state, is how the object is stored in memory. This is the object's nitty-gritty, the raw 1s and 0s that make up its data. An object is only physically constant if its representation in memory never changes.

Note that C++ bases constness on logical state, not physical state.

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;
}

Note that while you technically could use const_cast on this to make it non-cv-qualified, you really, REALLY shouldn't, and should use mutable instead. A const_cast is liable to invoke undefined behaviour when used on an object that actually is const, while mutable is designed to be safe to use. It is, however, possible that you may run into this in extremely old code.

An exception to this rule is defining non-cv-qualified accessors in terms of const accessors; as the object is guaranteed to not be const if the non-cv-qualified version is called, there's no risk of UB. (opens new window)

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 prevents unnecessary duplication of code.

As with regular pointers, if this is volatile (including const volatile), it is loaded from memory each time it is accessed, instead of being cached. This has the same effects on optimisation as declaring any other pointer volatile would, so care should be taken.

Note that if an instance is cv-qualified, the only member functions it is allowed to access are member functions whose this pointer is at least as cv-qualified as the instance itself:

  • Non-cv instances can access any member functions.
  • const instances can access const and const volatile functions.
  • volatile instances can access volatile and const volatile functions.
  • const volatile instances can access const volatile functions.

This is one of the key tenets of const correctness (opens new window).

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.

  this Pointer Ref-Qualifiers

Similarly to this cv-qualifiers, we can also apply ref-qualifiers to *this. Ref-qualifiers are used to choose between normal and rvalue reference semantics, allowing the compiler to use either copy or move semantics depending on which are more appropriate, and are applied to *this instead of this.

Note that despite ref-qualifiers using reference syntax, this itself is still a pointer. Also note that ref-qualifiers don't actually change the type of *this; it's just easier to describe and understand their effects by looking at them as if they did.

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

A member function cannot have overloads both with and without ref-qualifiers; the programmer has to choose between one or the other. Thankfully, cv-qualifiers can be used in conjunction with ref-qualifiers, allowing const correctness (opens new window) rules to be followed.

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&& {}
};

Remarks

The this pointer is a keyword for C++ therfore there is no library needed to implement this. And do not forget this is a pointer! So you cannot do:


this.someMember();

As you access member functions or member variables from pointers using the arrow symbol -> :

this->someMember();

Other helpful links to the better understanding of the this pointer :

What is the 'this' pointer? (opens new window)

http://www.geeksforgeeks.org/this-pointer-in-c/ (opens new window)

https://www.tutorialspoint.com/cplusplus/cpp_this_pointer.htm (opens new window)


Read Article
https://develop.horje.com/learn/1434/reference