How to Change the Variable to Which a C++ Reference Refers

How can I change the variable to which a C++ reference refers?

This is not possible, and that's by design. References cannot be rebound.

Is the practice of returning a C++ reference variable evil?

In general, returning a reference is perfectly normal and happens all the time.

If you mean:

int& getInt() {
int i;
return i; // DON'T DO THIS.
}

That is all sorts of evil. The stack-allocated i will go away and you are referring to nothing. This is also evil:

int& getInt() {
int* i = new int;
return *i; // DON'T DO THIS.
}

Because now the client has to eventually do the strange:

int& myInt = getInt(); // note the &, we cannot lose this reference!
delete &myInt; // must delete...totally weird and evil

int oops = getInt();
delete &oops; // undefined behavior, we're wrongly deleting a copy, not the original

Note that rvalue references are still just references, so all the evil applications remain the same.

If you want to allocate something that lives beyond the scope of the function, use a smart pointer (or in general, a container):

std::unique_ptr<int> getInt() {
return std::make_unique<int>(0);
}

And now the client stores a smart pointer:

std::unique_ptr<int> x = getInt();

References are also okay for accessing things where you know the lifetime is being kept open on a higher-level, e.g.:

struct immutableint {
immutableint(int i) : i_(i) {}

const int& get() const { return i_; }
private:
int i_;
};

Here we know it's okay to return a reference to i_ because whatever is calling us manages the lifetime of the class instance, so i_ will live at least that long.

And of course, there's nothing wrong with just:

int getInt() {
return 0;
}

If the lifetime should be left up to the caller, and you're just computing the value.

Summary: it's okay to return a reference if the lifetime of the object won't end after the call.

Passing by reference in C

Because you're passing the value of the pointer to the method and then dereferencing it to get the integer that is pointed to.

How do I pass a variable by reference?

Arguments are passed by assignment. The rationale behind this is twofold:

  1. the parameter passed in is actually a reference to an object (but the reference is passed by value)
  2. some data types are mutable, but others aren't

So:

  • If you pass a mutable object into a method, the method gets a reference to that same object and you can mutate it to your heart's delight, but if you rebind the reference in the method, the outer scope will know nothing about it, and after you're done, the outer reference will still point at the original object.

  • If you pass an immutable object to a method, you still can't rebind the outer reference, and you can't even mutate the object.

To make it even more clear, let's have some examples.

List - a mutable type

Let's try to modify the list that was passed to a method:

def try_to_change_list_contents(the_list):
print('got', the_list)
the_list.append('four')
print('changed to', the_list)

outer_list = ['one', 'two', 'three']

print('before, outer_list =', outer_list)
try_to_change_list_contents(outer_list)
print('after, outer_list =', outer_list)

Output:

before, outer_list = ['one', 'two', 'three']
got ['one', 'two', 'three']
changed to ['one', 'two', 'three', 'four']
after, outer_list = ['one', 'two', 'three', 'four']

Since the parameter passed in is a reference to outer_list, not a copy of it, we can use the mutating list methods to change it and have the changes reflected in the outer scope.

Now let's see what happens when we try to change the reference that was passed in as a parameter:

def try_to_change_list_reference(the_list):
print('got', the_list)
the_list = ['and', 'we', 'can', 'not', 'lie']
print('set to', the_list)

outer_list = ['we', 'like', 'proper', 'English']

print('before, outer_list =', outer_list)
try_to_change_list_reference(outer_list)
print('after, outer_list =', outer_list)

Output:

before, outer_list = ['we', 'like', 'proper', 'English']
got ['we', 'like', 'proper', 'English']
set to ['and', 'we', 'can', 'not', 'lie']
after, outer_list = ['we', 'like', 'proper', 'English']

Since the the_list parameter was passed by value, assigning a new list to it had no effect that the code outside the method could see. The the_list was a copy of the outer_list reference, and we had the_list point to a new list, but there was no way to change where outer_list pointed.

String - an immutable type

It's immutable, so there's nothing we can do to change the contents of the string

Now, let's try to change the reference

def try_to_change_string_reference(the_string):
print('got', the_string)
the_string = 'In a kingdom by the sea'
print('set to', the_string)

outer_string = 'It was many and many a year ago'

print('before, outer_string =', outer_string)
try_to_change_string_reference(outer_string)
print('after, outer_string =', outer_string)

Output:

before, outer_string = It was many and many a year ago
got It was many and many a year ago
set to In a kingdom by the sea
after, outer_string = It was many and many a year ago

Again, since the the_string parameter was passed by value, assigning a new string to it had no effect that the code outside the method could see. The the_string was a copy of the outer_string reference, and we had the_string point to a new string, but there was no way to change where outer_string pointed.

I hope this clears things up a little.

EDIT: It's been noted that this doesn't answer the question that @David originally asked, "Is there something I can do to pass the variable by actual reference?". Let's work on that.

How do we get around this?

As @Andrea's answer shows, you could return the new value. This doesn't change the way things are passed in, but does let you get the information you want back out:

def return_a_whole_new_string(the_string):
new_string = something_to_do_with_the_old_string(the_string)
return new_string

# then you could call it like
my_string = return_a_whole_new_string(my_string)

If you really wanted to avoid using a return value, you could create a class to hold your value and pass it into the function or use an existing class, like a list:

def use_a_wrapper_to_simulate_pass_by_reference(stuff_to_change):
new_string = something_to_do_with_the_old_string(stuff_to_change[0])
stuff_to_change[0] = new_string

# then you could call it like
wrapper = [my_string]
use_a_wrapper_to_simulate_pass_by_reference(wrapper)

do_something_with(wrapper[0])

Although this seems a little cumbersome.

Java changes reference but not the object itself?

Here is an illustration of your program and what it does. Each state of the system is shown separated by a dashed line.

Note what happens when you do t0 = t1. You assumed that this means that from now on, t0 and t1 are synonyms - whenever you use one, the other is affected. But in fact, t0 and t1 are simply two references to the same object.

Making a new assignment to t0 simply detached it from the "blue" object and re-attached it to the "violet" object.

As long as t0 and t1 were both pointing at the same object, any changes you make in the content of their pointed object (such as t0.i = 5) would be seen by the other reference, because they both refer to the same object. But as soon as you assign something else to t0, it loses its connection to t1. After you do that, changing it will be reflected in t2, which points to the same, "violet" object, not in t1 which still points to the old, "blue" one.

So:

  • If you assign a new value to the reference - it no longer points to the same object as before, and any previous "double referencing" is lost.
  • If you assign a new value to the content of the reference, it will be reflected in all reference variables that look at the same object.

This is an illustration of your program

How does a C++ reference look, memory-wise?

everywhere the reference j is encountered, it is replaced with the address of i. So basically the reference content address is resolved at compile time, and there is not need to dereference it like a pointer at run time.

Just to clarify what I mean by the address of i :

void function(int& x)
{
x = 10;
}

int main()
{
int i = 5;
int& j = i;

function(j);
}

In the above code, j should not take space on the main stack, but the reference x of function will take a place on its stack. That means when calling function with j as an argument, the address of i that will be pushed on the stack of function. The compiler can and should not reserve space on the main stack for j.

For the array part the standards say ::

C++ Standard 8.3.2/4:

There shall be no references to references, no arrays of references,
and no pointers to references.

Why arrays of references are illegal?

Call by value can change the value for main program

In f2 in the first program, pointers are being dereferenced to change what those pointers point to so that b and c in the main function are modified. In contrast, mystery in the second program is only swapping the values of the local variables ptra and ptrb which has no effect on the variables in main.

So this:

ptrb = ptra; 

Assigned the value of one pointer to another (which are both local to msytery), while this:

*a = *b;

Changes the value of what one pointer points to to the value of what another pointer points to, namely a and b in main.

Another way to think of this is with houses and their residents:

    ______        _____         ______       ______
/______\ /______\ /______\ /______\
2 |Smith | 4 |Jones | 6 | Bond | 8 | Solo |
-------- -------- -------- --------

The first program is saying "swap the residents of two houses":

    ______        _____         ______       ______
/______\ /______\ /______\ /______\
2 |Smith | 4 | Bond | 6 |Jones | 8 | Solo |
-------- -------- -------- --------

While the second is saying "swap the numbers on the houses":

    ______        _____         ______       ______
/______\ /______\ /______\ /______\
2 |Smith | 6 |Jones | 4 | Bond | 8 | Solo |
-------- -------- -------- --------


Related Topics



Leave a reply



Submit