Is the “this” pointer just a compile time thing?
up vote
28
down vote
favorite
I asked myself whether the this
pointer could be overused since I usually use it every single time I refer to a member variable or function. I wondered if it could have performance impact since there must be a pointer which needs to be dereferenced every time. So I wrote some test code
struct A {
int x;
A(int X) {
x = X; /* And a second time with this->x = X; */
}
};
int main() {
A a(8);
return 0;
}
and surprisingly even with -O0
they output the exact same assembler code.
Also if I use a member function and call it in another member function it shows the same behavior. So is the this
pointer just a compile time thing and not an actual pointer? Or are there cases where this
is actually translated and dereferenced? I use GCC 4.4.3 btw.
c++ gcc this this-pointer
add a comment |
up vote
28
down vote
favorite
I asked myself whether the this
pointer could be overused since I usually use it every single time I refer to a member variable or function. I wondered if it could have performance impact since there must be a pointer which needs to be dereferenced every time. So I wrote some test code
struct A {
int x;
A(int X) {
x = X; /* And a second time with this->x = X; */
}
};
int main() {
A a(8);
return 0;
}
and surprisingly even with -O0
they output the exact same assembler code.
Also if I use a member function and call it in another member function it shows the same behavior. So is the this
pointer just a compile time thing and not an actual pointer? Or are there cases where this
is actually translated and dereferenced? I use GCC 4.4.3 btw.
c++ gcc this this-pointer
6
Possible duplicate of Is there overhead using this-> in c++?
– underscore_d
yesterday
Comments are not for extended discussion; this conversation has been moved to chat.
– Samuel Liew♦
8 hours ago
add a comment |
up vote
28
down vote
favorite
up vote
28
down vote
favorite
I asked myself whether the this
pointer could be overused since I usually use it every single time I refer to a member variable or function. I wondered if it could have performance impact since there must be a pointer which needs to be dereferenced every time. So I wrote some test code
struct A {
int x;
A(int X) {
x = X; /* And a second time with this->x = X; */
}
};
int main() {
A a(8);
return 0;
}
and surprisingly even with -O0
they output the exact same assembler code.
Also if I use a member function and call it in another member function it shows the same behavior. So is the this
pointer just a compile time thing and not an actual pointer? Or are there cases where this
is actually translated and dereferenced? I use GCC 4.4.3 btw.
c++ gcc this this-pointer
I asked myself whether the this
pointer could be overused since I usually use it every single time I refer to a member variable or function. I wondered if it could have performance impact since there must be a pointer which needs to be dereferenced every time. So I wrote some test code
struct A {
int x;
A(int X) {
x = X; /* And a second time with this->x = X; */
}
};
int main() {
A a(8);
return 0;
}
and surprisingly even with -O0
they output the exact same assembler code.
Also if I use a member function and call it in another member function it shows the same behavior. So is the this
pointer just a compile time thing and not an actual pointer? Or are there cases where this
is actually translated and dereferenced? I use GCC 4.4.3 btw.
c++ gcc this this-pointer
c++ gcc this this-pointer
edited 2 days ago
sds
38k1492164
38k1492164
asked 2 days ago
Yastanub
390212
390212
6
Possible duplicate of Is there overhead using this-> in c++?
– underscore_d
yesterday
Comments are not for extended discussion; this conversation has been moved to chat.
– Samuel Liew♦
8 hours ago
add a comment |
6
Possible duplicate of Is there overhead using this-> in c++?
– underscore_d
yesterday
Comments are not for extended discussion; this conversation has been moved to chat.
– Samuel Liew♦
8 hours ago
6
6
Possible duplicate of Is there overhead using this-> in c++?
– underscore_d
yesterday
Possible duplicate of Is there overhead using this-> in c++?
– underscore_d
yesterday
Comments are not for extended discussion; this conversation has been moved to chat.
– Samuel Liew♦
8 hours ago
Comments are not for extended discussion; this conversation has been moved to chat.
– Samuel Liew♦
8 hours ago
add a comment |
11 Answers
11
active
oldest
votes
up vote
64
down vote
accepted
So is the this pointer just a compile time thing and not an actual pointer?
It very much is a run time thing. It refers to the object on which the member function is invoked, naturally that object can exist at run time.
What is a compile time thing is how name lookup works. When a compiler encounters x = X
it must figure out what is this x
that is being assigned. So it looks it up, and finds the member variable. Since this->x
and x
refer to the same thing, naturally you get the same assembly output.
Comments are not for extended discussion; this conversation has been moved to chat.
– Samuel Liew♦
8 hours ago
add a comment |
up vote
23
down vote
It is an actual pointer, as the standard specifies it (§12.2.2.1):
In the body of a non-static (12.2.1) member function, the keyword
this
is a prvalue expression whose value is the address of the object for which the function is called. The type ofthis
in a member function of a classX
isX*
.
this
is actually implicit every time you reference a non-static member variable or member function within a class own code. It is also needed (either when implicit or explicit) because the compiler needs to tie back the function or the variable to an actual object at runtime.
Using it explicitly is rarely useful, unless you need, for example, to disambiguate between a parameter and a member variable within a member function. Otherwise, without it the compiler will shadow the member variable with the parameter (See it live on Coliru).
6
You also need to explicitly writethis->
when accessing a member of a non-dependent base type from a template member. Not often needed, and a good compiler will diagnose exactly when you forget it, but worth mentioning.
– Toby Speight
yesterday
It can also be very useful to write "this->" when developing with an IDE, because the IDE can then provide a list of members to select from. (Personally, I tend not to use an IDE, but if one chooses to, taking advantage of it seems sensible.)
– Martin Bonner
yesterday
2
"Using it explicitly is rarely useful", from the compiler perspective, true; From a human perspective, some teams will enforce this as a style rule to prevent human-error introduced bugs.
– Tezra
yesterday
add a comment |
up vote
14
down vote
this
always has to exist when you are in a non-static method. Whether you explicitly use it or not, you have to have a reference to the current instance, and this is what this
gives you.
In both cases, you are going to access memory through the this
pointer. It's just that you can omit it in some cases.
Essentially, syntactical sugar (whether by inclusion or omission, its a shortcut).
– Draco18s
2 days ago
add a comment |
up vote
13
down vote
This is almost a duplicate of How do objects work in x86 at the assembly level?, where I comment the asm output of some examples, including showing which register the this
pointer was passed in.
In asm, this
works exactly like a hidden first arg, so both the member-function foo::add(int)
and the non-member add
which takes an explicit foo*
first arg compile to exactly the same asm.
struct foo {
int m;
void add(int a); // not inline so we get a stand-alone definition emitted
};
void foo::add(int a) {
this->m += a;
}
void add(foo *obj, int a) {
obj->m += a;
}
On the Godbolt compiler explorer, compiling for x86-64 with the System V ABI (first arg in RDI, second in RSI), we get:
# gcc8.2 -O3
foo::add(int):
add DWORD PTR [rdi], esi # memory-destination add
ret
add(foo*, int):
add DWORD PTR [rdi], esi
ret
I use GCC 4.4.3
That was released in January 2010, so it's missing nearly a decade of improvements to the optimizer, and to error messages. The gcc7 series has been out and stable for a while. Expect missed optimizations with such an old compiler, especially for modern instruction sets like AVX.
add a comment |
up vote
9
down vote
After compilation, every symbol is just an address, so it can't be a run-time issue.
Any member symbol is compiled to an offset in the current class anyway, even if you didn't use this
.
When name
is used in C++ it can be one of the following.
- In the global namespace (like
::name
), or in the current namespace, or in the used namespace (whenusing namespace ...
been used) - In the current class
- Local definition, in upper block
- Local definition, in current block
Therefore, when you write code, the compiler should scan each, in a manner to look for the symbol name, from the current block and up to the global namespace.
Using this->name
helps the compiler to narrow the search for name
to only look for it in the current class scope, meaning it skips local definitions, and if not found in class scope, do not look for it in the global scope.
add a comment |
up vote
5
down vote
Here is a simple example how "this" could be useful during runtime:
#include <vector>
#include <string>
#include <iostream>
class A;
typedef std::vector<A*> News;
class A
{
public:
A(const char* n): name(n){}
std::string name;
void subscribe(News& n)
{
n.push_back(this);
}
};
int main()
{
A a1("Alex"), a2("Bob"), a3("Chris");
News news;
a1.subscribe(news);
a3.subscribe(news);
std::cout << "Subscriber:";
for(auto& a: news)
{
std::cout << " " << a->name;
}
return 0;
}
add a comment |
up vote
3
down vote
since I usually use it every single time I refer to a member variable or function.
You always use this
when you refer to a member variable or function. There is simply no other way to reach members. The only choice is implicit vs explicit notation.
Let's go back to see how it was done before this
to understand what this
is.
Without OOP:
struct A {
int x;
};
void foo(A* that) {
bar(that->x)
}
With OOP but writing this
explicitly
struct A {
int x;
void foo(void) {
bar(this->x)
}
};
using shorter notation:
struct A {
int x;
void foo(void) {
bar(x)
}
};
But the difference is only in source code. All are compiled to same thing. If you create a member method, the compiler will create a pointer argument for you and name it "this". If you omit this->
when referring to a member, the compiler is clever just enough to insert it for you most of the time. That's it. The only difference is 6 less letters in the source.
Writing this
explicitly makes sense when there is an ambiguity, namely another variable named just like your member variable:
struct A {
int x;
A(int x) {
this->x = x
}
};
There are some instances, like __thiscall, where OO and non-OO code may end bit different in asm, but whenever the pointer is passed on stack and then optimized to a register or in ECX from the very beginning doesn't make it "not a pointer".
add a comment |
up vote
2
down vote
Your machine does not know anything about class methods, they are normal functions under the hood.
Hence methods have to be implemented by always passing a pointer to the current object, it's just implicit in C++, i.e. T Class::method(...)
is just syntactic sugar for T Class_Method(Class* this, ...)
.
Other languages like Python or Lua choose to make it explicit and modern object-oriented C APIs like Vulkan (unlike OpenGL) use a similar pattern.
add a comment |
up vote
2
down vote
"this" can also safeguard against shadowing by a function parameter, for example:
class Vector {
public:
double x,y,z;
void SetLocation(double x, double y, double z);
};
void Vector::SetLocation(double x, double y, double z) {
this->x = x; //Passed parameter assigned to member variable
this->y = y;
this->z = z;
}
(Obviously, writing such code is discouraged.)
1
Usually shadowing comes up as an issue when the member variable is being shadowed by an introduced local variable (where you normally aren't thinking of what is in the global scope), so use of this->x is encouraged to prevent such modification bugs.
– Tezra
yesterday
Yeah unfortunately -Wshadow is not enabled with -Wall. gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
– Trass3r
16 hours ago
add a comment |
up vote
2
down vote
if the compiler inlines a member function that is called with static rather than dynamic binding, it might be able to optimize away the this
pointer. Take this simple example:
#include <iostream>
using std::cout;
using std::endl;
class example {
public:
int foo() const { return x; }
int foo(const int i) { return (x = i); }
private:
int x;
};
int main(void)
{
example e;
e.foo(10);
cout << e.foo() << endl;
}
GCC 7.3.0 with the -march=x86-64 -O -S
flag is able to compile cout << e.foo()
to three instructions:
movl $10, %esi
leaq _ZSt4cout(%rip), %rdi
call _ZNSolsEi@PLT
This is a call to std::ostream::operator<<
. Remember that cout << e.foo();
is syntactic sugar for std::ostream::operator<< (cout, e.foo());
. And operator<<(int)
could be written two ways: static operator<< (ostream&, int)
, as a non-member function, where the operand on the left is an explicit parameter, or operator<<(int)
, as a member function, where it’s implicitly this
.
The compiler was able to deduce that e.foo()
will always be the constant 10
. Since the 64-bit x86 calling convention is to pass function arguments in registers, that compiles down to the single movl
instruction, which sets the second function parameter to 10
. The leaq
instruction sets the first argument (which might be an explicit ostream&
or the implicit this
) to &cout
. Then the program makes a call
to the function.
In more complex cases, though—such as if you have a function taking an example&
as a parameter—the compiler needs to look up this
, as this
is what tells the program which instance it’s working with, and therefore, which instance’s x
data member to look up.
Consider this example:
class example {
public:
int foo() const { return x; }
int foo(const int i) { return (x = i); }
private:
int x;
};
int bar( const example& e )
{
return e.foo();
}
The function bar()
gets compiled to a bit of boilerplate and the instruction:
movl (%rdi), %eax
ret
You remember from the previous example that %rdi
on x86-64 is the first function argument, the implicit this
pointer for the call to e.foo()
. Putting it in parentheses, (%rdi)
, means look up the variable at that location. (Since the only data in an example
instance is x
, &e.x
happens to be the same as &e
in this case.) Moving the contents to %eax
sets the return value.
In this case, the compiler needed the implicit this
argument to foo(/* example* this */)
to be able to find &e
and therefore &e.x
. In fact, inside a member function (that isn’t static
), x
, this->x
and (*this).x
all mean the same thing.
add a comment |
up vote
1
down vote
this
is a pointer. It's like an implicit parameter that's part of every method. You could imagine using plain C functions and writing code like:
Socket makeSocket(int port) { ... }
void send(Socket *this, Value v) { ... }
Value receive(Socket *this) { ... }
Socket *mySocket = makeSocket(1234);
send(mySocket, someValue); // The subject, `mySocket`, is passed in as a param called "this", explicitly
Value newData = receive(socket);
In C++, similar code might look like:
mySocket.send(someValue); // The subject, `mySocket`, is passed in as a param called "this"
Value newData = mySocket.receive();
add a comment |
11 Answers
11
active
oldest
votes
11 Answers
11
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
64
down vote
accepted
So is the this pointer just a compile time thing and not an actual pointer?
It very much is a run time thing. It refers to the object on which the member function is invoked, naturally that object can exist at run time.
What is a compile time thing is how name lookup works. When a compiler encounters x = X
it must figure out what is this x
that is being assigned. So it looks it up, and finds the member variable. Since this->x
and x
refer to the same thing, naturally you get the same assembly output.
Comments are not for extended discussion; this conversation has been moved to chat.
– Samuel Liew♦
8 hours ago
add a comment |
up vote
64
down vote
accepted
So is the this pointer just a compile time thing and not an actual pointer?
It very much is a run time thing. It refers to the object on which the member function is invoked, naturally that object can exist at run time.
What is a compile time thing is how name lookup works. When a compiler encounters x = X
it must figure out what is this x
that is being assigned. So it looks it up, and finds the member variable. Since this->x
and x
refer to the same thing, naturally you get the same assembly output.
Comments are not for extended discussion; this conversation has been moved to chat.
– Samuel Liew♦
8 hours ago
add a comment |
up vote
64
down vote
accepted
up vote
64
down vote
accepted
So is the this pointer just a compile time thing and not an actual pointer?
It very much is a run time thing. It refers to the object on which the member function is invoked, naturally that object can exist at run time.
What is a compile time thing is how name lookup works. When a compiler encounters x = X
it must figure out what is this x
that is being assigned. So it looks it up, and finds the member variable. Since this->x
and x
refer to the same thing, naturally you get the same assembly output.
So is the this pointer just a compile time thing and not an actual pointer?
It very much is a run time thing. It refers to the object on which the member function is invoked, naturally that object can exist at run time.
What is a compile time thing is how name lookup works. When a compiler encounters x = X
it must figure out what is this x
that is being assigned. So it looks it up, and finds the member variable. Since this->x
and x
refer to the same thing, naturally you get the same assembly output.
answered 2 days ago
StoryTeller
88.6k12177245
88.6k12177245
Comments are not for extended discussion; this conversation has been moved to chat.
– Samuel Liew♦
8 hours ago
add a comment |
Comments are not for extended discussion; this conversation has been moved to chat.
– Samuel Liew♦
8 hours ago
Comments are not for extended discussion; this conversation has been moved to chat.
– Samuel Liew♦
8 hours ago
Comments are not for extended discussion; this conversation has been moved to chat.
– Samuel Liew♦
8 hours ago
add a comment |
up vote
23
down vote
It is an actual pointer, as the standard specifies it (§12.2.2.1):
In the body of a non-static (12.2.1) member function, the keyword
this
is a prvalue expression whose value is the address of the object for which the function is called. The type ofthis
in a member function of a classX
isX*
.
this
is actually implicit every time you reference a non-static member variable or member function within a class own code. It is also needed (either when implicit or explicit) because the compiler needs to tie back the function or the variable to an actual object at runtime.
Using it explicitly is rarely useful, unless you need, for example, to disambiguate between a parameter and a member variable within a member function. Otherwise, without it the compiler will shadow the member variable with the parameter (See it live on Coliru).
6
You also need to explicitly writethis->
when accessing a member of a non-dependent base type from a template member. Not often needed, and a good compiler will diagnose exactly when you forget it, but worth mentioning.
– Toby Speight
yesterday
It can also be very useful to write "this->" when developing with an IDE, because the IDE can then provide a list of members to select from. (Personally, I tend not to use an IDE, but if one chooses to, taking advantage of it seems sensible.)
– Martin Bonner
yesterday
2
"Using it explicitly is rarely useful", from the compiler perspective, true; From a human perspective, some teams will enforce this as a style rule to prevent human-error introduced bugs.
– Tezra
yesterday
add a comment |
up vote
23
down vote
It is an actual pointer, as the standard specifies it (§12.2.2.1):
In the body of a non-static (12.2.1) member function, the keyword
this
is a prvalue expression whose value is the address of the object for which the function is called. The type ofthis
in a member function of a classX
isX*
.
this
is actually implicit every time you reference a non-static member variable or member function within a class own code. It is also needed (either when implicit or explicit) because the compiler needs to tie back the function or the variable to an actual object at runtime.
Using it explicitly is rarely useful, unless you need, for example, to disambiguate between a parameter and a member variable within a member function. Otherwise, without it the compiler will shadow the member variable with the parameter (See it live on Coliru).
6
You also need to explicitly writethis->
when accessing a member of a non-dependent base type from a template member. Not often needed, and a good compiler will diagnose exactly when you forget it, but worth mentioning.
– Toby Speight
yesterday
It can also be very useful to write "this->" when developing with an IDE, because the IDE can then provide a list of members to select from. (Personally, I tend not to use an IDE, but if one chooses to, taking advantage of it seems sensible.)
– Martin Bonner
yesterday
2
"Using it explicitly is rarely useful", from the compiler perspective, true; From a human perspective, some teams will enforce this as a style rule to prevent human-error introduced bugs.
– Tezra
yesterday
add a comment |
up vote
23
down vote
up vote
23
down vote
It is an actual pointer, as the standard specifies it (§12.2.2.1):
In the body of a non-static (12.2.1) member function, the keyword
this
is a prvalue expression whose value is the address of the object for which the function is called. The type ofthis
in a member function of a classX
isX*
.
this
is actually implicit every time you reference a non-static member variable or member function within a class own code. It is also needed (either when implicit or explicit) because the compiler needs to tie back the function or the variable to an actual object at runtime.
Using it explicitly is rarely useful, unless you need, for example, to disambiguate between a parameter and a member variable within a member function. Otherwise, without it the compiler will shadow the member variable with the parameter (See it live on Coliru).
It is an actual pointer, as the standard specifies it (§12.2.2.1):
In the body of a non-static (12.2.1) member function, the keyword
this
is a prvalue expression whose value is the address of the object for which the function is called. The type ofthis
in a member function of a classX
isX*
.
this
is actually implicit every time you reference a non-static member variable or member function within a class own code. It is also needed (either when implicit or explicit) because the compiler needs to tie back the function or the variable to an actual object at runtime.
Using it explicitly is rarely useful, unless you need, for example, to disambiguate between a parameter and a member variable within a member function. Otherwise, without it the compiler will shadow the member variable with the parameter (See it live on Coliru).
edited yesterday
Toby Speight
15.8k133965
15.8k133965
answered 2 days ago
JBL
9,52433567
9,52433567
6
You also need to explicitly writethis->
when accessing a member of a non-dependent base type from a template member. Not often needed, and a good compiler will diagnose exactly when you forget it, but worth mentioning.
– Toby Speight
yesterday
It can also be very useful to write "this->" when developing with an IDE, because the IDE can then provide a list of members to select from. (Personally, I tend not to use an IDE, but if one chooses to, taking advantage of it seems sensible.)
– Martin Bonner
yesterday
2
"Using it explicitly is rarely useful", from the compiler perspective, true; From a human perspective, some teams will enforce this as a style rule to prevent human-error introduced bugs.
– Tezra
yesterday
add a comment |
6
You also need to explicitly writethis->
when accessing a member of a non-dependent base type from a template member. Not often needed, and a good compiler will diagnose exactly when you forget it, but worth mentioning.
– Toby Speight
yesterday
It can also be very useful to write "this->" when developing with an IDE, because the IDE can then provide a list of members to select from. (Personally, I tend not to use an IDE, but if one chooses to, taking advantage of it seems sensible.)
– Martin Bonner
yesterday
2
"Using it explicitly is rarely useful", from the compiler perspective, true; From a human perspective, some teams will enforce this as a style rule to prevent human-error introduced bugs.
– Tezra
yesterday
6
6
You also need to explicitly write
this->
when accessing a member of a non-dependent base type from a template member. Not often needed, and a good compiler will diagnose exactly when you forget it, but worth mentioning.– Toby Speight
yesterday
You also need to explicitly write
this->
when accessing a member of a non-dependent base type from a template member. Not often needed, and a good compiler will diagnose exactly when you forget it, but worth mentioning.– Toby Speight
yesterday
It can also be very useful to write "this->" when developing with an IDE, because the IDE can then provide a list of members to select from. (Personally, I tend not to use an IDE, but if one chooses to, taking advantage of it seems sensible.)
– Martin Bonner
yesterday
It can also be very useful to write "this->" when developing with an IDE, because the IDE can then provide a list of members to select from. (Personally, I tend not to use an IDE, but if one chooses to, taking advantage of it seems sensible.)
– Martin Bonner
yesterday
2
2
"Using it explicitly is rarely useful", from the compiler perspective, true; From a human perspective, some teams will enforce this as a style rule to prevent human-error introduced bugs.
– Tezra
yesterday
"Using it explicitly is rarely useful", from the compiler perspective, true; From a human perspective, some teams will enforce this as a style rule to prevent human-error introduced bugs.
– Tezra
yesterday
add a comment |
up vote
14
down vote
this
always has to exist when you are in a non-static method. Whether you explicitly use it or not, you have to have a reference to the current instance, and this is what this
gives you.
In both cases, you are going to access memory through the this
pointer. It's just that you can omit it in some cases.
Essentially, syntactical sugar (whether by inclusion or omission, its a shortcut).
– Draco18s
2 days ago
add a comment |
up vote
14
down vote
this
always has to exist when you are in a non-static method. Whether you explicitly use it or not, you have to have a reference to the current instance, and this is what this
gives you.
In both cases, you are going to access memory through the this
pointer. It's just that you can omit it in some cases.
Essentially, syntactical sugar (whether by inclusion or omission, its a shortcut).
– Draco18s
2 days ago
add a comment |
up vote
14
down vote
up vote
14
down vote
this
always has to exist when you are in a non-static method. Whether you explicitly use it or not, you have to have a reference to the current instance, and this is what this
gives you.
In both cases, you are going to access memory through the this
pointer. It's just that you can omit it in some cases.
this
always has to exist when you are in a non-static method. Whether you explicitly use it or not, you have to have a reference to the current instance, and this is what this
gives you.
In both cases, you are going to access memory through the this
pointer. It's just that you can omit it in some cases.
edited 2 days ago
answered 2 days ago
Matthieu Brucher
5,4161128
5,4161128
Essentially, syntactical sugar (whether by inclusion or omission, its a shortcut).
– Draco18s
2 days ago
add a comment |
Essentially, syntactical sugar (whether by inclusion or omission, its a shortcut).
– Draco18s
2 days ago
Essentially, syntactical sugar (whether by inclusion or omission, its a shortcut).
– Draco18s
2 days ago
Essentially, syntactical sugar (whether by inclusion or omission, its a shortcut).
– Draco18s
2 days ago
add a comment |
up vote
13
down vote
This is almost a duplicate of How do objects work in x86 at the assembly level?, where I comment the asm output of some examples, including showing which register the this
pointer was passed in.
In asm, this
works exactly like a hidden first arg, so both the member-function foo::add(int)
and the non-member add
which takes an explicit foo*
first arg compile to exactly the same asm.
struct foo {
int m;
void add(int a); // not inline so we get a stand-alone definition emitted
};
void foo::add(int a) {
this->m += a;
}
void add(foo *obj, int a) {
obj->m += a;
}
On the Godbolt compiler explorer, compiling for x86-64 with the System V ABI (first arg in RDI, second in RSI), we get:
# gcc8.2 -O3
foo::add(int):
add DWORD PTR [rdi], esi # memory-destination add
ret
add(foo*, int):
add DWORD PTR [rdi], esi
ret
I use GCC 4.4.3
That was released in January 2010, so it's missing nearly a decade of improvements to the optimizer, and to error messages. The gcc7 series has been out and stable for a while. Expect missed optimizations with such an old compiler, especially for modern instruction sets like AVX.
add a comment |
up vote
13
down vote
This is almost a duplicate of How do objects work in x86 at the assembly level?, where I comment the asm output of some examples, including showing which register the this
pointer was passed in.
In asm, this
works exactly like a hidden first arg, so both the member-function foo::add(int)
and the non-member add
which takes an explicit foo*
first arg compile to exactly the same asm.
struct foo {
int m;
void add(int a); // not inline so we get a stand-alone definition emitted
};
void foo::add(int a) {
this->m += a;
}
void add(foo *obj, int a) {
obj->m += a;
}
On the Godbolt compiler explorer, compiling for x86-64 with the System V ABI (first arg in RDI, second in RSI), we get:
# gcc8.2 -O3
foo::add(int):
add DWORD PTR [rdi], esi # memory-destination add
ret
add(foo*, int):
add DWORD PTR [rdi], esi
ret
I use GCC 4.4.3
That was released in January 2010, so it's missing nearly a decade of improvements to the optimizer, and to error messages. The gcc7 series has been out and stable for a while. Expect missed optimizations with such an old compiler, especially for modern instruction sets like AVX.
add a comment |
up vote
13
down vote
up vote
13
down vote
This is almost a duplicate of How do objects work in x86 at the assembly level?, where I comment the asm output of some examples, including showing which register the this
pointer was passed in.
In asm, this
works exactly like a hidden first arg, so both the member-function foo::add(int)
and the non-member add
which takes an explicit foo*
first arg compile to exactly the same asm.
struct foo {
int m;
void add(int a); // not inline so we get a stand-alone definition emitted
};
void foo::add(int a) {
this->m += a;
}
void add(foo *obj, int a) {
obj->m += a;
}
On the Godbolt compiler explorer, compiling for x86-64 with the System V ABI (first arg in RDI, second in RSI), we get:
# gcc8.2 -O3
foo::add(int):
add DWORD PTR [rdi], esi # memory-destination add
ret
add(foo*, int):
add DWORD PTR [rdi], esi
ret
I use GCC 4.4.3
That was released in January 2010, so it's missing nearly a decade of improvements to the optimizer, and to error messages. The gcc7 series has been out and stable for a while. Expect missed optimizations with such an old compiler, especially for modern instruction sets like AVX.
This is almost a duplicate of How do objects work in x86 at the assembly level?, where I comment the asm output of some examples, including showing which register the this
pointer was passed in.
In asm, this
works exactly like a hidden first arg, so both the member-function foo::add(int)
and the non-member add
which takes an explicit foo*
first arg compile to exactly the same asm.
struct foo {
int m;
void add(int a); // not inline so we get a stand-alone definition emitted
};
void foo::add(int a) {
this->m += a;
}
void add(foo *obj, int a) {
obj->m += a;
}
On the Godbolt compiler explorer, compiling for x86-64 with the System V ABI (first arg in RDI, second in RSI), we get:
# gcc8.2 -O3
foo::add(int):
add DWORD PTR [rdi], esi # memory-destination add
ret
add(foo*, int):
add DWORD PTR [rdi], esi
ret
I use GCC 4.4.3
That was released in January 2010, so it's missing nearly a decade of improvements to the optimizer, and to error messages. The gcc7 series has been out and stable for a while. Expect missed optimizations with such an old compiler, especially for modern instruction sets like AVX.
answered 2 days ago
Peter Cordes
114k16173297
114k16173297
add a comment |
add a comment |
up vote
9
down vote
After compilation, every symbol is just an address, so it can't be a run-time issue.
Any member symbol is compiled to an offset in the current class anyway, even if you didn't use this
.
When name
is used in C++ it can be one of the following.
- In the global namespace (like
::name
), or in the current namespace, or in the used namespace (whenusing namespace ...
been used) - In the current class
- Local definition, in upper block
- Local definition, in current block
Therefore, when you write code, the compiler should scan each, in a manner to look for the symbol name, from the current block and up to the global namespace.
Using this->name
helps the compiler to narrow the search for name
to only look for it in the current class scope, meaning it skips local definitions, and if not found in class scope, do not look for it in the global scope.
add a comment |
up vote
9
down vote
After compilation, every symbol is just an address, so it can't be a run-time issue.
Any member symbol is compiled to an offset in the current class anyway, even if you didn't use this
.
When name
is used in C++ it can be one of the following.
- In the global namespace (like
::name
), or in the current namespace, or in the used namespace (whenusing namespace ...
been used) - In the current class
- Local definition, in upper block
- Local definition, in current block
Therefore, when you write code, the compiler should scan each, in a manner to look for the symbol name, from the current block and up to the global namespace.
Using this->name
helps the compiler to narrow the search for name
to only look for it in the current class scope, meaning it skips local definitions, and if not found in class scope, do not look for it in the global scope.
add a comment |
up vote
9
down vote
up vote
9
down vote
After compilation, every symbol is just an address, so it can't be a run-time issue.
Any member symbol is compiled to an offset in the current class anyway, even if you didn't use this
.
When name
is used in C++ it can be one of the following.
- In the global namespace (like
::name
), or in the current namespace, or in the used namespace (whenusing namespace ...
been used) - In the current class
- Local definition, in upper block
- Local definition, in current block
Therefore, when you write code, the compiler should scan each, in a manner to look for the symbol name, from the current block and up to the global namespace.
Using this->name
helps the compiler to narrow the search for name
to only look for it in the current class scope, meaning it skips local definitions, and if not found in class scope, do not look for it in the global scope.
After compilation, every symbol is just an address, so it can't be a run-time issue.
Any member symbol is compiled to an offset in the current class anyway, even if you didn't use this
.
When name
is used in C++ it can be one of the following.
- In the global namespace (like
::name
), or in the current namespace, or in the used namespace (whenusing namespace ...
been used) - In the current class
- Local definition, in upper block
- Local definition, in current block
Therefore, when you write code, the compiler should scan each, in a manner to look for the symbol name, from the current block and up to the global namespace.
Using this->name
helps the compiler to narrow the search for name
to only look for it in the current class scope, meaning it skips local definitions, and if not found in class scope, do not look for it in the global scope.
edited yesterday
Peter Mortensen
13.3k1983111
13.3k1983111
answered 2 days ago
SHR
5,48242240
5,48242240
add a comment |
add a comment |
up vote
5
down vote
Here is a simple example how "this" could be useful during runtime:
#include <vector>
#include <string>
#include <iostream>
class A;
typedef std::vector<A*> News;
class A
{
public:
A(const char* n): name(n){}
std::string name;
void subscribe(News& n)
{
n.push_back(this);
}
};
int main()
{
A a1("Alex"), a2("Bob"), a3("Chris");
News news;
a1.subscribe(news);
a3.subscribe(news);
std::cout << "Subscriber:";
for(auto& a: news)
{
std::cout << " " << a->name;
}
return 0;
}
add a comment |
up vote
5
down vote
Here is a simple example how "this" could be useful during runtime:
#include <vector>
#include <string>
#include <iostream>
class A;
typedef std::vector<A*> News;
class A
{
public:
A(const char* n): name(n){}
std::string name;
void subscribe(News& n)
{
n.push_back(this);
}
};
int main()
{
A a1("Alex"), a2("Bob"), a3("Chris");
News news;
a1.subscribe(news);
a3.subscribe(news);
std::cout << "Subscriber:";
for(auto& a: news)
{
std::cout << " " << a->name;
}
return 0;
}
add a comment |
up vote
5
down vote
up vote
5
down vote
Here is a simple example how "this" could be useful during runtime:
#include <vector>
#include <string>
#include <iostream>
class A;
typedef std::vector<A*> News;
class A
{
public:
A(const char* n): name(n){}
std::string name;
void subscribe(News& n)
{
n.push_back(this);
}
};
int main()
{
A a1("Alex"), a2("Bob"), a3("Chris");
News news;
a1.subscribe(news);
a3.subscribe(news);
std::cout << "Subscriber:";
for(auto& a: news)
{
std::cout << " " << a->name;
}
return 0;
}
Here is a simple example how "this" could be useful during runtime:
#include <vector>
#include <string>
#include <iostream>
class A;
typedef std::vector<A*> News;
class A
{
public:
A(const char* n): name(n){}
std::string name;
void subscribe(News& n)
{
n.push_back(this);
}
};
int main()
{
A a1("Alex"), a2("Bob"), a3("Chris");
News news;
a1.subscribe(news);
a3.subscribe(news);
std::cout << "Subscriber:";
for(auto& a: news)
{
std::cout << " " << a->name;
}
return 0;
}
answered 2 days ago
Helmut Zeisel
663
663
add a comment |
add a comment |
up vote
3
down vote
since I usually use it every single time I refer to a member variable or function.
You always use this
when you refer to a member variable or function. There is simply no other way to reach members. The only choice is implicit vs explicit notation.
Let's go back to see how it was done before this
to understand what this
is.
Without OOP:
struct A {
int x;
};
void foo(A* that) {
bar(that->x)
}
With OOP but writing this
explicitly
struct A {
int x;
void foo(void) {
bar(this->x)
}
};
using shorter notation:
struct A {
int x;
void foo(void) {
bar(x)
}
};
But the difference is only in source code. All are compiled to same thing. If you create a member method, the compiler will create a pointer argument for you and name it "this". If you omit this->
when referring to a member, the compiler is clever just enough to insert it for you most of the time. That's it. The only difference is 6 less letters in the source.
Writing this
explicitly makes sense when there is an ambiguity, namely another variable named just like your member variable:
struct A {
int x;
A(int x) {
this->x = x
}
};
There are some instances, like __thiscall, where OO and non-OO code may end bit different in asm, but whenever the pointer is passed on stack and then optimized to a register or in ECX from the very beginning doesn't make it "not a pointer".
add a comment |
up vote
3
down vote
since I usually use it every single time I refer to a member variable or function.
You always use this
when you refer to a member variable or function. There is simply no other way to reach members. The only choice is implicit vs explicit notation.
Let's go back to see how it was done before this
to understand what this
is.
Without OOP:
struct A {
int x;
};
void foo(A* that) {
bar(that->x)
}
With OOP but writing this
explicitly
struct A {
int x;
void foo(void) {
bar(this->x)
}
};
using shorter notation:
struct A {
int x;
void foo(void) {
bar(x)
}
};
But the difference is only in source code. All are compiled to same thing. If you create a member method, the compiler will create a pointer argument for you and name it "this". If you omit this->
when referring to a member, the compiler is clever just enough to insert it for you most of the time. That's it. The only difference is 6 less letters in the source.
Writing this
explicitly makes sense when there is an ambiguity, namely another variable named just like your member variable:
struct A {
int x;
A(int x) {
this->x = x
}
};
There are some instances, like __thiscall, where OO and non-OO code may end bit different in asm, but whenever the pointer is passed on stack and then optimized to a register or in ECX from the very beginning doesn't make it "not a pointer".
add a comment |
up vote
3
down vote
up vote
3
down vote
since I usually use it every single time I refer to a member variable or function.
You always use this
when you refer to a member variable or function. There is simply no other way to reach members. The only choice is implicit vs explicit notation.
Let's go back to see how it was done before this
to understand what this
is.
Without OOP:
struct A {
int x;
};
void foo(A* that) {
bar(that->x)
}
With OOP but writing this
explicitly
struct A {
int x;
void foo(void) {
bar(this->x)
}
};
using shorter notation:
struct A {
int x;
void foo(void) {
bar(x)
}
};
But the difference is only in source code. All are compiled to same thing. If you create a member method, the compiler will create a pointer argument for you and name it "this". If you omit this->
when referring to a member, the compiler is clever just enough to insert it for you most of the time. That's it. The only difference is 6 less letters in the source.
Writing this
explicitly makes sense when there is an ambiguity, namely another variable named just like your member variable:
struct A {
int x;
A(int x) {
this->x = x
}
};
There are some instances, like __thiscall, where OO and non-OO code may end bit different in asm, but whenever the pointer is passed on stack and then optimized to a register or in ECX from the very beginning doesn't make it "not a pointer".
since I usually use it every single time I refer to a member variable or function.
You always use this
when you refer to a member variable or function. There is simply no other way to reach members. The only choice is implicit vs explicit notation.
Let's go back to see how it was done before this
to understand what this
is.
Without OOP:
struct A {
int x;
};
void foo(A* that) {
bar(that->x)
}
With OOP but writing this
explicitly
struct A {
int x;
void foo(void) {
bar(this->x)
}
};
using shorter notation:
struct A {
int x;
void foo(void) {
bar(x)
}
};
But the difference is only in source code. All are compiled to same thing. If you create a member method, the compiler will create a pointer argument for you and name it "this". If you omit this->
when referring to a member, the compiler is clever just enough to insert it for you most of the time. That's it. The only difference is 6 less letters in the source.
Writing this
explicitly makes sense when there is an ambiguity, namely another variable named just like your member variable:
struct A {
int x;
A(int x) {
this->x = x
}
};
There are some instances, like __thiscall, where OO and non-OO code may end bit different in asm, but whenever the pointer is passed on stack and then optimized to a register or in ECX from the very beginning doesn't make it "not a pointer".
answered yesterday
Agent_L
3,1711620
3,1711620
add a comment |
add a comment |
up vote
2
down vote
Your machine does not know anything about class methods, they are normal functions under the hood.
Hence methods have to be implemented by always passing a pointer to the current object, it's just implicit in C++, i.e. T Class::method(...)
is just syntactic sugar for T Class_Method(Class* this, ...)
.
Other languages like Python or Lua choose to make it explicit and modern object-oriented C APIs like Vulkan (unlike OpenGL) use a similar pattern.
add a comment |
up vote
2
down vote
Your machine does not know anything about class methods, they are normal functions under the hood.
Hence methods have to be implemented by always passing a pointer to the current object, it's just implicit in C++, i.e. T Class::method(...)
is just syntactic sugar for T Class_Method(Class* this, ...)
.
Other languages like Python or Lua choose to make it explicit and modern object-oriented C APIs like Vulkan (unlike OpenGL) use a similar pattern.
add a comment |
up vote
2
down vote
up vote
2
down vote
Your machine does not know anything about class methods, they are normal functions under the hood.
Hence methods have to be implemented by always passing a pointer to the current object, it's just implicit in C++, i.e. T Class::method(...)
is just syntactic sugar for T Class_Method(Class* this, ...)
.
Other languages like Python or Lua choose to make it explicit and modern object-oriented C APIs like Vulkan (unlike OpenGL) use a similar pattern.
Your machine does not know anything about class methods, they are normal functions under the hood.
Hence methods have to be implemented by always passing a pointer to the current object, it's just implicit in C++, i.e. T Class::method(...)
is just syntactic sugar for T Class_Method(Class* this, ...)
.
Other languages like Python or Lua choose to make it explicit and modern object-oriented C APIs like Vulkan (unlike OpenGL) use a similar pattern.
answered yesterday
Trass3r
2,7501429
2,7501429
add a comment |
add a comment |
up vote
2
down vote
"this" can also safeguard against shadowing by a function parameter, for example:
class Vector {
public:
double x,y,z;
void SetLocation(double x, double y, double z);
};
void Vector::SetLocation(double x, double y, double z) {
this->x = x; //Passed parameter assigned to member variable
this->y = y;
this->z = z;
}
(Obviously, writing such code is discouraged.)
1
Usually shadowing comes up as an issue when the member variable is being shadowed by an introduced local variable (where you normally aren't thinking of what is in the global scope), so use of this->x is encouraged to prevent such modification bugs.
– Tezra
yesterday
Yeah unfortunately -Wshadow is not enabled with -Wall. gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
– Trass3r
16 hours ago
add a comment |
up vote
2
down vote
"this" can also safeguard against shadowing by a function parameter, for example:
class Vector {
public:
double x,y,z;
void SetLocation(double x, double y, double z);
};
void Vector::SetLocation(double x, double y, double z) {
this->x = x; //Passed parameter assigned to member variable
this->y = y;
this->z = z;
}
(Obviously, writing such code is discouraged.)
1
Usually shadowing comes up as an issue when the member variable is being shadowed by an introduced local variable (where you normally aren't thinking of what is in the global scope), so use of this->x is encouraged to prevent such modification bugs.
– Tezra
yesterday
Yeah unfortunately -Wshadow is not enabled with -Wall. gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
– Trass3r
16 hours ago
add a comment |
up vote
2
down vote
up vote
2
down vote
"this" can also safeguard against shadowing by a function parameter, for example:
class Vector {
public:
double x,y,z;
void SetLocation(double x, double y, double z);
};
void Vector::SetLocation(double x, double y, double z) {
this->x = x; //Passed parameter assigned to member variable
this->y = y;
this->z = z;
}
(Obviously, writing such code is discouraged.)
"this" can also safeguard against shadowing by a function parameter, for example:
class Vector {
public:
double x,y,z;
void SetLocation(double x, double y, double z);
};
void Vector::SetLocation(double x, double y, double z) {
this->x = x; //Passed parameter assigned to member variable
this->y = y;
this->z = z;
}
(Obviously, writing such code is discouraged.)
answered yesterday
Szak1
37539
37539
1
Usually shadowing comes up as an issue when the member variable is being shadowed by an introduced local variable (where you normally aren't thinking of what is in the global scope), so use of this->x is encouraged to prevent such modification bugs.
– Tezra
yesterday
Yeah unfortunately -Wshadow is not enabled with -Wall. gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
– Trass3r
16 hours ago
add a comment |
1
Usually shadowing comes up as an issue when the member variable is being shadowed by an introduced local variable (where you normally aren't thinking of what is in the global scope), so use of this->x is encouraged to prevent such modification bugs.
– Tezra
yesterday
Yeah unfortunately -Wshadow is not enabled with -Wall. gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
– Trass3r
16 hours ago
1
1
Usually shadowing comes up as an issue when the member variable is being shadowed by an introduced local variable (where you normally aren't thinking of what is in the global scope), so use of this->x is encouraged to prevent such modification bugs.
– Tezra
yesterday
Usually shadowing comes up as an issue when the member variable is being shadowed by an introduced local variable (where you normally aren't thinking of what is in the global scope), so use of this->x is encouraged to prevent such modification bugs.
– Tezra
yesterday
Yeah unfortunately -Wshadow is not enabled with -Wall. gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
– Trass3r
16 hours ago
Yeah unfortunately -Wshadow is not enabled with -Wall. gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
– Trass3r
16 hours ago
add a comment |
up vote
2
down vote
if the compiler inlines a member function that is called with static rather than dynamic binding, it might be able to optimize away the this
pointer. Take this simple example:
#include <iostream>
using std::cout;
using std::endl;
class example {
public:
int foo() const { return x; }
int foo(const int i) { return (x = i); }
private:
int x;
};
int main(void)
{
example e;
e.foo(10);
cout << e.foo() << endl;
}
GCC 7.3.0 with the -march=x86-64 -O -S
flag is able to compile cout << e.foo()
to three instructions:
movl $10, %esi
leaq _ZSt4cout(%rip), %rdi
call _ZNSolsEi@PLT
This is a call to std::ostream::operator<<
. Remember that cout << e.foo();
is syntactic sugar for std::ostream::operator<< (cout, e.foo());
. And operator<<(int)
could be written two ways: static operator<< (ostream&, int)
, as a non-member function, where the operand on the left is an explicit parameter, or operator<<(int)
, as a member function, where it’s implicitly this
.
The compiler was able to deduce that e.foo()
will always be the constant 10
. Since the 64-bit x86 calling convention is to pass function arguments in registers, that compiles down to the single movl
instruction, which sets the second function parameter to 10
. The leaq
instruction sets the first argument (which might be an explicit ostream&
or the implicit this
) to &cout
. Then the program makes a call
to the function.
In more complex cases, though—such as if you have a function taking an example&
as a parameter—the compiler needs to look up this
, as this
is what tells the program which instance it’s working with, and therefore, which instance’s x
data member to look up.
Consider this example:
class example {
public:
int foo() const { return x; }
int foo(const int i) { return (x = i); }
private:
int x;
};
int bar( const example& e )
{
return e.foo();
}
The function bar()
gets compiled to a bit of boilerplate and the instruction:
movl (%rdi), %eax
ret
You remember from the previous example that %rdi
on x86-64 is the first function argument, the implicit this
pointer for the call to e.foo()
. Putting it in parentheses, (%rdi)
, means look up the variable at that location. (Since the only data in an example
instance is x
, &e.x
happens to be the same as &e
in this case.) Moving the contents to %eax
sets the return value.
In this case, the compiler needed the implicit this
argument to foo(/* example* this */)
to be able to find &e
and therefore &e.x
. In fact, inside a member function (that isn’t static
), x
, this->x
and (*this).x
all mean the same thing.
add a comment |
up vote
2
down vote
if the compiler inlines a member function that is called with static rather than dynamic binding, it might be able to optimize away the this
pointer. Take this simple example:
#include <iostream>
using std::cout;
using std::endl;
class example {
public:
int foo() const { return x; }
int foo(const int i) { return (x = i); }
private:
int x;
};
int main(void)
{
example e;
e.foo(10);
cout << e.foo() << endl;
}
GCC 7.3.0 with the -march=x86-64 -O -S
flag is able to compile cout << e.foo()
to three instructions:
movl $10, %esi
leaq _ZSt4cout(%rip), %rdi
call _ZNSolsEi@PLT
This is a call to std::ostream::operator<<
. Remember that cout << e.foo();
is syntactic sugar for std::ostream::operator<< (cout, e.foo());
. And operator<<(int)
could be written two ways: static operator<< (ostream&, int)
, as a non-member function, where the operand on the left is an explicit parameter, or operator<<(int)
, as a member function, where it’s implicitly this
.
The compiler was able to deduce that e.foo()
will always be the constant 10
. Since the 64-bit x86 calling convention is to pass function arguments in registers, that compiles down to the single movl
instruction, which sets the second function parameter to 10
. The leaq
instruction sets the first argument (which might be an explicit ostream&
or the implicit this
) to &cout
. Then the program makes a call
to the function.
In more complex cases, though—such as if you have a function taking an example&
as a parameter—the compiler needs to look up this
, as this
is what tells the program which instance it’s working with, and therefore, which instance’s x
data member to look up.
Consider this example:
class example {
public:
int foo() const { return x; }
int foo(const int i) { return (x = i); }
private:
int x;
};
int bar( const example& e )
{
return e.foo();
}
The function bar()
gets compiled to a bit of boilerplate and the instruction:
movl (%rdi), %eax
ret
You remember from the previous example that %rdi
on x86-64 is the first function argument, the implicit this
pointer for the call to e.foo()
. Putting it in parentheses, (%rdi)
, means look up the variable at that location. (Since the only data in an example
instance is x
, &e.x
happens to be the same as &e
in this case.) Moving the contents to %eax
sets the return value.
In this case, the compiler needed the implicit this
argument to foo(/* example* this */)
to be able to find &e
and therefore &e.x
. In fact, inside a member function (that isn’t static
), x
, this->x
and (*this).x
all mean the same thing.
add a comment |
up vote
2
down vote
up vote
2
down vote
if the compiler inlines a member function that is called with static rather than dynamic binding, it might be able to optimize away the this
pointer. Take this simple example:
#include <iostream>
using std::cout;
using std::endl;
class example {
public:
int foo() const { return x; }
int foo(const int i) { return (x = i); }
private:
int x;
};
int main(void)
{
example e;
e.foo(10);
cout << e.foo() << endl;
}
GCC 7.3.0 with the -march=x86-64 -O -S
flag is able to compile cout << e.foo()
to three instructions:
movl $10, %esi
leaq _ZSt4cout(%rip), %rdi
call _ZNSolsEi@PLT
This is a call to std::ostream::operator<<
. Remember that cout << e.foo();
is syntactic sugar for std::ostream::operator<< (cout, e.foo());
. And operator<<(int)
could be written two ways: static operator<< (ostream&, int)
, as a non-member function, where the operand on the left is an explicit parameter, or operator<<(int)
, as a member function, where it’s implicitly this
.
The compiler was able to deduce that e.foo()
will always be the constant 10
. Since the 64-bit x86 calling convention is to pass function arguments in registers, that compiles down to the single movl
instruction, which sets the second function parameter to 10
. The leaq
instruction sets the first argument (which might be an explicit ostream&
or the implicit this
) to &cout
. Then the program makes a call
to the function.
In more complex cases, though—such as if you have a function taking an example&
as a parameter—the compiler needs to look up this
, as this
is what tells the program which instance it’s working with, and therefore, which instance’s x
data member to look up.
Consider this example:
class example {
public:
int foo() const { return x; }
int foo(const int i) { return (x = i); }
private:
int x;
};
int bar( const example& e )
{
return e.foo();
}
The function bar()
gets compiled to a bit of boilerplate and the instruction:
movl (%rdi), %eax
ret
You remember from the previous example that %rdi
on x86-64 is the first function argument, the implicit this
pointer for the call to e.foo()
. Putting it in parentheses, (%rdi)
, means look up the variable at that location. (Since the only data in an example
instance is x
, &e.x
happens to be the same as &e
in this case.) Moving the contents to %eax
sets the return value.
In this case, the compiler needed the implicit this
argument to foo(/* example* this */)
to be able to find &e
and therefore &e.x
. In fact, inside a member function (that isn’t static
), x
, this->x
and (*this).x
all mean the same thing.
if the compiler inlines a member function that is called with static rather than dynamic binding, it might be able to optimize away the this
pointer. Take this simple example:
#include <iostream>
using std::cout;
using std::endl;
class example {
public:
int foo() const { return x; }
int foo(const int i) { return (x = i); }
private:
int x;
};
int main(void)
{
example e;
e.foo(10);
cout << e.foo() << endl;
}
GCC 7.3.0 with the -march=x86-64 -O -S
flag is able to compile cout << e.foo()
to three instructions:
movl $10, %esi
leaq _ZSt4cout(%rip), %rdi
call _ZNSolsEi@PLT
This is a call to std::ostream::operator<<
. Remember that cout << e.foo();
is syntactic sugar for std::ostream::operator<< (cout, e.foo());
. And operator<<(int)
could be written two ways: static operator<< (ostream&, int)
, as a non-member function, where the operand on the left is an explicit parameter, or operator<<(int)
, as a member function, where it’s implicitly this
.
The compiler was able to deduce that e.foo()
will always be the constant 10
. Since the 64-bit x86 calling convention is to pass function arguments in registers, that compiles down to the single movl
instruction, which sets the second function parameter to 10
. The leaq
instruction sets the first argument (which might be an explicit ostream&
or the implicit this
) to &cout
. Then the program makes a call
to the function.
In more complex cases, though—such as if you have a function taking an example&
as a parameter—the compiler needs to look up this
, as this
is what tells the program which instance it’s working with, and therefore, which instance’s x
data member to look up.
Consider this example:
class example {
public:
int foo() const { return x; }
int foo(const int i) { return (x = i); }
private:
int x;
};
int bar( const example& e )
{
return e.foo();
}
The function bar()
gets compiled to a bit of boilerplate and the instruction:
movl (%rdi), %eax
ret
You remember from the previous example that %rdi
on x86-64 is the first function argument, the implicit this
pointer for the call to e.foo()
. Putting it in parentheses, (%rdi)
, means look up the variable at that location. (Since the only data in an example
instance is x
, &e.x
happens to be the same as &e
in this case.) Moving the contents to %eax
sets the return value.
In this case, the compiler needed the implicit this
argument to foo(/* example* this */)
to be able to find &e
and therefore &e.x
. In fact, inside a member function (that isn’t static
), x
, this->x
and (*this).x
all mean the same thing.
edited 17 hours ago
answered yesterday
Davislor
8,22111126
8,22111126
add a comment |
add a comment |
up vote
1
down vote
this
is a pointer. It's like an implicit parameter that's part of every method. You could imagine using plain C functions and writing code like:
Socket makeSocket(int port) { ... }
void send(Socket *this, Value v) { ... }
Value receive(Socket *this) { ... }
Socket *mySocket = makeSocket(1234);
send(mySocket, someValue); // The subject, `mySocket`, is passed in as a param called "this", explicitly
Value newData = receive(socket);
In C++, similar code might look like:
mySocket.send(someValue); // The subject, `mySocket`, is passed in as a param called "this"
Value newData = mySocket.receive();
add a comment |
up vote
1
down vote
this
is a pointer. It's like an implicit parameter that's part of every method. You could imagine using plain C functions and writing code like:
Socket makeSocket(int port) { ... }
void send(Socket *this, Value v) { ... }
Value receive(Socket *this) { ... }
Socket *mySocket = makeSocket(1234);
send(mySocket, someValue); // The subject, `mySocket`, is passed in as a param called "this", explicitly
Value newData = receive(socket);
In C++, similar code might look like:
mySocket.send(someValue); // The subject, `mySocket`, is passed in as a param called "this"
Value newData = mySocket.receive();
add a comment |
up vote
1
down vote
up vote
1
down vote
this
is a pointer. It's like an implicit parameter that's part of every method. You could imagine using plain C functions and writing code like:
Socket makeSocket(int port) { ... }
void send(Socket *this, Value v) { ... }
Value receive(Socket *this) { ... }
Socket *mySocket = makeSocket(1234);
send(mySocket, someValue); // The subject, `mySocket`, is passed in as a param called "this", explicitly
Value newData = receive(socket);
In C++, similar code might look like:
mySocket.send(someValue); // The subject, `mySocket`, is passed in as a param called "this"
Value newData = mySocket.receive();
this
is a pointer. It's like an implicit parameter that's part of every method. You could imagine using plain C functions and writing code like:
Socket makeSocket(int port) { ... }
void send(Socket *this, Value v) { ... }
Value receive(Socket *this) { ... }
Socket *mySocket = makeSocket(1234);
send(mySocket, someValue); // The subject, `mySocket`, is passed in as a param called "this", explicitly
Value newData = receive(socket);
In C++, similar code might look like:
mySocket.send(someValue); // The subject, `mySocket`, is passed in as a param called "this"
Value newData = mySocket.receive();
answered 16 hours ago
Alexander
30.1k44474
30.1k44474
add a comment |
add a comment |
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53264848%2fis-the-this-pointer-just-a-compile-time-thing%23new-answer', 'question_page');
}
);
Post as a guest
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
6
Possible duplicate of Is there overhead using this-> in c++?
– underscore_d
yesterday
Comments are not for extended discussion; this conversation has been moved to chat.
– Samuel Liew♦
8 hours ago