Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does it matter if this is used in a C++ setter?

Tags:

c++

setter

this

Suppose I have a c++ class with a private variable, x. For it's setter, is there any difference using this? Is there the potential for unwanted / unexpected behavior is I don't use this?

Setter:

void setX(double input)
{
   x = input;
}

Setter using this:

void setX(double x)
{
   this->x = x;
}
like image 525
Elpezmuerto Avatar asked Sep 26 '11 15:09

Elpezmuerto


2 Answers

These two code fragments (assuming they're inline member functions, since there's no ClassName:: bit) are exactly equivalent. Use whichever you prefer. I would tend to recommend against naming parameters the same as member variables though; it's too easy get them mixed up.

like image 89
bdonlan Avatar answered Sep 17 '22 17:09

bdonlan


Because of two-phase lookup for class templates, this might be required to

  1. explicitly state you mean a member
  2. because of lookup rules, the compiler might assume another entity that is visible there is meant

(see the first example) Both are equivalent.

In a non-template situation, you usually avoid using this; the generated code will be the same, thus there won't be any difference (see the second example). The reason for this is that the compiler will try to lookup every symbol it sees. The first place to lookup is in class-scope (except for block and function scope). If it finds a symbol of that name there, it will emit this implicitly. In reality, member functions are just ordinary functions with an invisible parameter.

class Foo {
    void foo () { x = 0; }
    void bar () const { std::cout << x; }
    void frob();
    int x;
};
void Foo::frob() {}

Is actually transformed into

class Foo {        
    int x;
};
inline void foo (Foo *const this) { this->x = 0; }
inline void bar (Foo const * const this) { std::cout << this->x; }
void frob (Foo * const this) {}

by the compiler.


Behavioral example for this in templates:

#include <iostream>

void foo() {
    std::cout << "::foo()\n";
}

template <typename>
struct Base {
    void foo() const { std::cout << "Base<T>::foo()\n"; }
};

template <typename T>
struct Derived_using_this : Base<Derived_using_this<T> > {
    void bar() const { this->foo(); }
};

template <typename T>
struct Derived_not_using_this : Base<Derived_not_using_this<T> > {
    void bar() const { foo(); }
};


int main () {
    Derived_not_using_this<void>().bar();
    Derived_using_this<void>().bar();
}

Output:

::foo()
Base<T>::foo()

Example assembly for non-template:

With this:

pushq   %rbp
movq    %rsp, %rbp
subq    $16, %rsp
movq    %rdi, -8(%rbp)
movq    -8(%rbp), %rax
movl    (%rax), %eax
movl    %eax, %esi
movl    $.LC0, %edi
movl    $0, %eax
call    printf
leave
ret

Without this:

pushq   %rbp
movq    %rsp, %rbp
subq    $16, %rsp
movq    %rdi, -8(%rbp)
movq    -8(%rbp), %rax
movl    (%rax), %eax
movl    %eax, %esi
movl    $.LC0, %edi
movl    $0, %eax
call    printf
leave
ret

Verify yourself:

test.cc:

#include <stdio.h> // Don't use this. I just did so for nicer assembly.

class Foo {
public:
    Foo () : i(0) {}

    void with_this() const { printf ("%d", this->i); }
    void without_this() const { printf ("%d", i); }
private:
    int i;
};


int main () {
    Foo f;
    f.with_this();
    f.without_this();
}

Run g++ -S test.cc. You'll see a file named test.s, there you can search for the function names.

like image 37
Sebastian Mach Avatar answered Sep 17 '22 17:09

Sebastian Mach