简体   繁体   中英

Return by reference in c++

#include<iostream>
using namespace std;

int& add() {

    static int a;
    a++;
    cout<<"\na="<<a;
    return a;
}

int main {

   int x,y,m;
   int &z = add(); //it behaves as a reference to "a" which is a static variable
   x=add();//why does it work correctly
   y=add();//why does it work correctly
   z++;
   m=add();//why does it work correctly
   cout<<"\nx="<<x<<"\ny="<<y<<"\nz="<<z<<"\nm="<<m;
   return 0;
}

output:
a=1
a=2
a=3
a=5
x=2
y=3
z=5
m=5

What happens exactly internally for x,y,m & z

int main{
    int x,y,m;      // declare three ints:  x,y,m, all unassigned
    int &z = add(); // a is incremented to 1, z is a reference to a
    x=add();        // add() returns a reference to a, x copies the value of a
    y=add();        // same as above
    z++;            // increments a, since z is a reference to a
    m=add();        // same as x and y, copies the value of a
    cout<<"\nx="<<x<<"\ny="<<y<<"\nz="<<z<<"\nm="<<m;
    return 0;
}

so you call add() three times in a row, which increments a to 1, 2, 3 (and prints the value each time.

z is a reference to the real a .

x , y , and m are copies of the value of a when the assignment was made. So x is 2, y is 3, we skip 4 by doing z++ , and then m is 5.

int &z = add();

z is a reference to the static variable a . From now on, a and z are synonym. Both of them point to same memory location.

int x;
x = add();

x is an independent memory location. Because of the assignment, x will hold whatever add() returns at the point of calling. This is how y and m also works.

x , y , and m are just variables, so they get whatever value is in a at the time add is called, and that's it. If you were to change the values of any of those variables, it would not affect the value of a . Since z is a reference, it is a "pointer" (in a sense) to the static a variable, so changing z will actually change a .

A reference is another name, an alias, for some object. It acts like if it was the object.

When add() returns a reference to a , it behaves like if the returned value was a .

So the code

x = add();

behaves like it had been

a++;
x = a;

So it captures the value a has at that moment.

On the other hand, the code

int& z = add();

behaves just like

a++;
int& z = a;

Where z becomes another name for a . Using a or z from now on doesn't matter, it means the same. There is still only one value, but the value has two names. We can use either one to to read it or update it.

It's just like when we use Bob to refer to a guy named Robert - another name for the same thing.

I believe this is how it works:

References used in context where (a value of) the referenced type is needed are implicitly dereferenced. This includes (among others):

  • assignment to a variable
  • passing function arguments declared by value
  • passing function return values declared by value

In the above contexts, a copy of the referenced value is created.

Wherever a reference is needed, they are treated as references (obviously not dereferenced.) This includes:

  • initializing a reference of compatible type
  • passing function arguments declared by reference
  • passing function return values if declared by reference

In these contexts, no copy of the referenced value is created. The new reference is just a new alias for the existing value.

The syntax for both of the above contexts is the same, so it requires some effort to see what's going on.

A reference is just a constant pointer, in the sense that you can change the pointed object but you cannot make it to point to something else.

What C++ does is just saving you from adding pointing de-referencing syntax (unary * ) when you need the value and allows using . instead of -> to access members of the pointed-to structured type.

Note that there is a common anti-pattern in C++ that uses const X& as if that was a smarter way than just X for function parameters. Please never forget that a reference is basically a pointer so the pointed object may change or even disappear while being used ( const doesn't mean the object doesn't change, just that you cannot change it using that reference ).

See this answer for a more detailed description of why you should pay attention when using references instead of values.

Every time you see a reference you should think at least a bit if the object is going to live enough and/or mutate while you are using it. In your case the referenced object is a function static, therefore there are no problems with lifetime (function static objects come into existence the first time you enter the scope where they are defined and live until the end of the program).

Translating your code to what really happens (pointer fiddling) you get

#include<iostream>
using namespace std;

int* add() {
    static int a;
    a++;
    cout << "\na=" << a;
    return &a; // we're returning a pointer to our static variable
}

int main(int argc, const char *argv[]) {
   int x,y,m;
   int *z = add(); // points to "a" which is a static variable
   x = *add();     // just get the current value of (incremented) a
   y = *add();     // same here, of course now different from x
   (*z)++;         // incrementing a from outside the function (we've a pointer!)
   m = *add();     // once again just reading current incremented value
   cout << "\nx=" << x << "\ny=" << y<< "\nz=" << (*z) << "\nm=" << m;
   return 0;
}

Ok, let's dissect this small program

int& add() 
{
   static int a;
   a++;
   cout<<"\na="<<a;
   return a;
}

static int a declares a variable. Static in this context means that there is only on place to store a and that place is used whenever add is called. So a is like a global variable that can only be accessed inside add.

The return value is a reference. Most compiler use pointer internally to represent reference. What you can see here is that a reference to the static variable a is returned. Anyone can change a once he obtained the reference.

The a++ and the cout is straightforward - no further comments here.

int main{
    int x,y,m;      // declare three ints:  x,y,m, all unassigned
    int &z = add(); // a is incremented to 1, z is a reference to a
    x=add();        // add() returns a reference to a, x copies the value of a
    y=add();        // same as above
    z++;            // increments a, since z is a reference to a
    m=add();        // same as x and y, copies the value of a
    cout<<"\nx="<<x<<"\ny="<<y<<"\nz="<<z<<"\nm="<<m;
    return 0;
}

int &z = add(); calls add - which means that a is incremented. z is now an alias for the static variable a since it uses the reference. x=add(); increment a again but now copy the value of a since x is not of type int& but of type int. y - same. z++: since z is an alias for a - increment a without printing it via cout.

m=add(): increment a, copy a int m like x and y.

The output is straightforward once you understand why x,y,m are copies and z is an alias.

Any questions still open?

regards

Tobias

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM