int a_value = 2;
int result1 = incr1(a_value); // no reference, a copy, local to the function, is made.
assert(a_value == 2); // ok
assert(result1 == 3); // ok
int result2 = incr2(a_value); // with reference, there is no copy made.
assert(a_value == 2); // error, the original a_value parameter as been incremented.
assert(a_value == 3); // ok
assert(result2 == 3); // ok
}
int incr2(int& value) { value = value + 1; return value; }
is the same as
int incr3(int* value) { (*value) = (*value) + 1; return *value; }
and they will be used diferently for the same result :
void main() {
}
in the call to incr3 "&a_value" can be read : address_of "a_value".
and in the body of incr3 "*value" can be read : content of address "value".
and finnaly in the prototype "int incr3(int* value)", "int*" can be read : pointer to an int, or address of an int.
pointer/address are a fundamental concept for programming at large, the concept of "indirection".
all this may seem unrelated at first, but in your Complex problem, you can try something like :
#include <iostream>
...
Complex c2;
std::cout << &c2 /* address of c2 */ << std::endl;
and in your copy constructor :
Complex(Complex& c) {
}
you will see that, with a reference, the addresses are the sames, but without references you will get different addresses meaning that your variables are'nt the same (in the case of function parameters/arguments they are copies of the parameters values, that's why we call this "pass by value" semantic, rather than "pass by reference" or "pass by address" or box, or indirection, or whatever having the same meaning).
hope this help.