Download presentation
Presentation is loading. Please wait.
Published byBonnie Dean Modified over 6 years ago
1
Reference variables, pass-by-reference and return-by-reference
2
Outline In this lesson, we will: Learn about reference variables
Aliases to other assignable items (lvalues) See how to use this for pass-by-reference Changing arguments—not parameters—inside of functions Useful for updating arguments that hold values We will also see return-by-reference
3
Reference local variables
In C++, it is possible to declare a local variable to be an alias or a reference to another variable #include <iostream> int main(); int main() { int m{42}; int &n{m}; // Now, 'n' is an alias for 'm' std::cout << "m = " << m << ", \tn = " << n << std::endl; m = 91; n = 360; return 0; } Output: m = 42, n = 42 m = 91, n = 91 m = 360, n = 360
4
Aliases Such a variable is said to be a reference variable or an alias
A reference variable must be initialized with an lvalue That is, something that can be assigned a value Any access or assignment to a reference variable changes the original It is not possible to assign a new alias… Recall that an alias is another name for the same item Lewis Carroll was an alias for Charles Dodgson Mark Twain was an alias for Samuel Clemens Praising the writing skills of either Carroll or Twain was identical to praising the skills of Dodgson or Clemens, respectively
5
Reference local variables
Array entries are assignable, so this works: #include <iostream> int main(); int main() { int data[10]{}; int &first{data[0]}; int &last{data[9]}; first = 42; last = 91; for ( std::size_t k{0}; k < 10; ++k ) { std::cout << data[k] << std::endl; } return 0; Output: 42 91
6
Pass-by-value As we have seen, when calling a function, the value of the arguments are copied to the appropriate location on the stack The parameters are variables local to the function The function can treat the parameters just like local variables
7
Pass-by-value Compare these two:
If an array is passed as an argument, only the address is passed It doesn’t matter if you pass an array of capacity 1 or 1 million If an instance of a struct is passed as an argument, all the member variables are copied It will take longer if there are many member variables Question: Can we pass primitive data types and structures in a manner similar to arrays? Answer: As you may guess, yes
8
Pass-by-reference If a parameter is prefixed by an &, this indicates the argument is passed-by-reference Arguments are restricted to lvalues: something that can be assigned E.g., variables and array entries The argument is not copied, but rather, the parameter identifier becomes an alias for the argument Any change to the parameter changes the original argument
9
Pass-by-reference Example: Any argument is passed by reference Output:
void reset( int &n ); void reset( int &n ) { n = 0; } Any argument is passed by reference A change to the parameter n also changes the argument int main() { int k{42}; reset( k ); std::cout << k << std::endl; return 0; Output:
10
Pass-by-reference Only those items that can appear on the left-hand side of assignment statements can be passed by reference: int main() { int k{42}; reset( k + 1 ); std::cout << k << std::endl; return 0; } example.cpp: In function 'int main()': example.cpp:12:11: error: invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int' reset( k + 1 ); ^ example.cpp:6:6: error: in passing argument 1 of 'void reset(int&)' void reset( int &n ) {
11
Pass-by-reference When you perform a std::cin statement, the second operand is passed by reference, so it can be modified: int main() { int k; std::cout << "Enter an integer: " << std::endl; std::cin >> k; std::cout << k << "*" << k << " = " << (k*k) << std::endl; return 0; }
12
Applications We will look at four applications:
Having multiple return values Verifying if a returned value is valid Updating a variable subject to conditions Updating and accessing instances of structures
13
Application: multiple return values
Suppose you need both the minimum and maximum of three values: void min_max( int a, int b, int c, int &min, int &max ) { if ( a < b ) { min = a; max = b; } else { min = b; max = a; } if ( c < min ) { min = c; } else if ( c > max ) { max = c;
14
Application: Is a returned value valid?
Suppose you want to check if arithmetic calculations are valid All integers are valid return values, yet under- or overflow may occur int add( int a, int b, bool &invalid_result ) { int result{ a + b }; // The result is invalid if // 'a' and 'b' are positive yet 'a + b' is negative, or // 'a' and 'b' are negative yet 'a + b' is positive invalid_result = ( ((a > 0) && (b > 0) && (result <= 0)) || ((a < 0) && (b < 0) && (result >= 0)) ); return result; } int multiply( int a, int b, bool &invalid_result ) { int result{ a*b }; // The result is invalid if '(a*b)/b != a' for a non-zero 'b' // - a*0 = 0 for all possible values of 'a' invalid_result = ( (b != 0) && (result/b != a) );
15
Application: Updating a variable
Suppose you want to increment a variable that cycles through an array of a given capacity: std::size_t k{0}; while ( some-condition ) { // Do something... // Move 'k' to next entry of an array, but // if we are at the end of the array, go back to 0 if ( k == (array_capacity - 1) ) { k = 0; } else { ++k; }
16
Application: Updating a variable
Instead, let’s write a function that increments its argument appropriately: void increment_and_cycle( std::size_t &n, std::size_t capacity ) { if ( n == (capacity - 1) ) { n = 0; } else { ++n; } Now the original code is much more clear: std::size_t k{0}; while ( some-condition ) { // Do something... increment_and_cycle( k, array_capacity );
17
Application: Passing structures
Suppose we have our vector_3d_t data structure: We can now write a function that normalizes the a vector struct vector_3d_t; void normalize( vector_3d_t &v ); void noramlize( vector_3d_t &v ) { double norm_v{norm( v )}; if ( norm_v != 0.0 ) { v.x /= norm_v; v.y /= norm_v; v.z /= norm_v; }
18
Application: Passing structures
We can now normalize vectors with a function call: struct vector_3d_t; void normalize( vector_3d_t &v ); double norm( vector_3d_t v ); int main(); int main() { vector_3d_t dir{-1.0, 2.0, 4.0}; vector_print( dir ); std::cout << "||dir|| = " << norm( dir ) << std::endl; normalize( dir ); return 0; } Output: (-1,2,4) ||dir|| = ( , , ) ||dir|| = 1
19
Application: Passing structures
Try it yourself: #include <iostream> #include <cmath> struct vector_3d_t; double norm( vector_3d_t v ); void normalize( vector_3d_t &v ); void print_vector( vector_3d_t v ); int main(); struct vector_3d_t { double x; double y; double z; }; int main() { vector_3d_t dir{-1.0, 2.0, 4.0}; print_vector( dir ); std::cout << "||dir|| = " << norm( dir ) << std::endl; normalize( dir ); return 0; } double norm( vector_3d_t v ) { return std::sqrt( v.x*v.x + v.y*v.y + v.z*v.z ); void normalize( vector_3d_t &v ) { double norm_v{norm( v )}; if ( norm_v != 0.0 ) { v.x /= norm_v; v.y /= norm_v; v.z /= norm_v; void print_vector( vector_3d_t v ) { std::cout << "(" << v.x << "," << v.y << "," << v.z << ")" << std::endl;
20
Application: Passing structures
Suppose we have a larger structure: struct bank_account_t { unsigned long unique_id; unsigned long user_id; long balance; // in cents unsigned long overdraft_protection_amount; // in cents }; If you pass this structure to a function, a copy is made Any change to the member variables of the parameter is not reflected in the argument
21
Application: Passing structures
For example, void deposit( bank_account_t acct, unsigned int amount ); void deposit( bank_account_t acct, unsigned int amount ) { acct.balance += amount; } If you call // Deposit one million dollars deposit( prof_patels_account, ); Prof. Patel will be disappointed—there is no change to his account…
22
Application: Passing structures
Thus, you require the instance to be passed by value void deposit( bank_account_t &acct, unsigned long amount ); void deposit( bank_account_t &acct, unsigned long amount ) { acct.balance += amount; } Now, if you call for a deposit, it happens: // Deposit one million dollars deposit( prof_harders_account, );
23
Application: Passing structures
Even a simple Boolean-valued check is faster with a pass-by-reference: bool is_in_overdraft( bank_account_t &acct ); return (acct.balance < 0); } We didn’t change the account, but we didn’t have to make a copy of it
24
Return-by-reference It is even possible to return by reference:
int &f( int &n ); int &f( int &n ) { std::cout << "In f(1): " << n << std::endl; n = 360; std::cout << "In f(2): " << n << std::endl; return n; } Now consider int main() { int k{42}; f(k) = 91; std::cout << k << std::endl; return 0;
25
Return-by-reference Is this valid? Why or why not?
int &f(); int &f() { int n; return n; } Now consider what would happen if we tried: int main() { f() = 91; return 0;
26
Return-by-reference When you call a std::cout statement, std::cout is passed by reference, and once again returned by reference, which is why you multiple items being printed: int main() { std::cout << "3 + 4 = " << (3 + 4) << std::endl; return 0; } Yes, std::cout; is a valid statement—it does nothing ((std::cout << "3 + 4 = ") << (3 + 4)) << std::endl; ( std::cout << (3 + 4)) << std::endl; std::cout << std::endl; Returned by reference std::cout;
27
In this course… In this course, we will only use pass-by-reference
We generally will not use reference local variables We will see, but not use, return-by-reference
28
Summary Following this lesson, you now
Know how to create an alias or reference to another assignable variable Understand that a parameter can be an alias to the argument This is know as pass-by-reference Are aware of numerous applications of pass-by-reference Returning more information than one return value allows Useful with passing instances of structures Know that there is also a return-by-reference
29
References [1] No references?
30
Colophon These slides were prepared using the Georgia typeface. Mathematical equations use Times New Roman, and source code is presented using Consolas. The photographs of lilacs in bloom appearing on the title slide and accenting the top of each other slide were taken at the Royal Botanical Gardens on May 27, 2018 by Douglas Wilhelm Harder. Please see for more information.
31
Disclaimer These slides are provided for the ece 150 Fundamentals of Programming course taught at the University of Waterloo. The material in it reflects the authors’ best judgment in light of the information available to them at the time of preparation. Any reliance on these course slides by any party for any other purpose are the responsibility of such parties. The authors accept no responsibility for damages, if any, suffered by any party as a result of decisions made or actions based on these course slides for any other purpose than that for which it was intended.
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.