Reputation: 84
I have read many articles on this question, from which I get that references are only aliases and they don't consume any memory. Compiler replace the reference with the address of pointing variable.
Can any one explain what will happen for below example. How compiler work with reference ri?
int main()
{
int *pi = new int(50);
int &ri = *pi;
ri = 30;
cout << "val = " << ri << " , " << *pi << endl;
}
It gives me the output:
val = 30 , 30
Upvotes: 4
Views: 2389
Reputation: 9
Reference variable takes memory on the stack , Best way to test this is using assembly of a c++ program as shown below.... write a c++ program as shown below and generate a assembly of it
#include<iostream>
using namespace std;
int main(){
int i = 100;
int j = 200;
int &x1 = i;
int &x2 = i;
int &x3 = i;
int &x4 = i;
int &x5 = i;
int &x6 = i;
int &x7 = i;
cout << "reference value" << x1 << endl;
return 0;
}
Assembly of this program shows that stack pointer moved down upto 36 byte "which means reference gets memory".
main:
.LFB966:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
pushl %ebx
andl $-16, %esp
subl $64, %esp
movl $100, 28(%esp)
If you change above cpp program as below
#include<iostream>
using namespace std;
int main(){
int i = 100;
int j = 200;
int &x1 = i;
cout << "ref changed" << x1 << endl;
return 0;
}
Assembly of it showing stack pointer movement of 12 bytes..
main:
.LFB966:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
pushl %ebx
andl $-16, %esp
subl $32, %esp
movl $100, 20(%esp)
I believe above practical clears every thing, Correct me if I am wrong .. :)
Upvotes: 0
Reputation: 300029
References are defined as aliases. The Standard does not specify how they are represented, though the implementations do not vary much. Essentially:
Let us see how it translates, starting with your program:
int main()
{
int *pi = new int(50);
int &ri = *pi;
ri = 30;
std::cout << "val = " << ri << " , " << *pi << std::endl;
}
We can eliminate ri
, because the object it is bound to is known by the compiler:
int main()
{
int *pi = new int(50);
*pi = 30;
std::cout << "val = " << *pi << " , " << *pi << std::endl;
}
We can eliminate *pi
because its final value is known by the compiler:
int main() {
new int(50); // stupid possible side effect usually forbid to optimize this out
std::cout << "val = " << 30 << " , " << 30 << std::endl;
}
I would note that in your example, the new
call is completely useless, you can also reference objects that have not been dynamically allocated.
int main() {
int i = 50;
int& ri = i;
ri = 30;
std::cout << "val = " << ri << " < " << i << std::endl;
}
was equally valid, and without memory leak.
Getting back to our distinction between the representations:
void swap(Foo& left, Foo& right);
is typically implemented as:
void swap(Foo* left, Foo* right);
In this case, the reference ends up taking (some) space (as much as a pointer).
On the other hand with:
class Object {
public:
Object(): foo(f), f() {}
Foo const& foo;
void set(Foo const& value);
private:
Foo f;
};
Compiler will typically not give foo
a runtime representation. The fact that it is a const
reference will be used to restrict the possible methods invoked on f
to those not changing it (semantic difference) but at runtime they will directly be passed f
.
Upvotes: 2
Reputation: 678
int *pi = new int(50);
you allocate an int object 50;
int &ri = *pi;
you set a alias ri to this int object, ri is this object and pi is the address of the object;
ri = 30;
reassign 30 to the int object; remember ri is the int object;
cout << "val = " << ri << " , " << *pi << endl;
ri and *pi are the same object. You just remember an object could have many aliases , and using anyone of these aliases can manipulate the object.
and where is the delete.
Upvotes: 1
Reputation: 25505
ri is a stack variable that works just like a pointer that you can't assign to a new memory address. It is mostly a semantic sugar there is nothing you can do with a reference you can't do with a pointer you can just do it more safely. When this function terminate the space utilized by ri will be cleaned up off the stack.
Upvotes: 0
Reputation: 36102
int *pi = new int(50);
+----+
pi --> | 50 |
+----+
int &ri = *pi;
+----+
pi --> | 50 | ri = 50, same as *pi, which is 50
+----+
ri = 30; now the contents of what pi points to i.e. *pi is replaced with 30
+----+
pi --> | 30 |
+----+
Upvotes: 2
Reputation: 361612
The compiler might replace the reference with the actual object as:
int main()
{
int *pi = new int(50);
//int &ri = *pi; //compiler might remove this
*pi = 30; //then it replaces ri with *pi
cout << "val = " << *pi << " , " << *pi << endl; //here as well
}
This is one thing that compiler might do.
Upvotes: 1