1-4) Values, References and Pointers

Revisions:
May 20, modified code examples so they compile with visual studio c++ 2005 express edition. Modified example code has keywords in color.



C++ provides three types of variables or paramters

BankAccount b; //value
BankAccount *b; //pointer
BankAccount &b; //references

foo(BankAccount b) {...} //pass by value
foo(BankAccount *b) {...} //pass by pointer
foo(BankAccount &b) {...} //pass by reference

BankAccount deposit(float amt) {...} //return by value
BankAccount & deposit(float amt) {...} //return by reference
BankAccount * deposit(float amt) {...} //return by pointer

Do not confuse the use of &:

  BankAccount &b;   //Declares b as a reference variable
  p = &b;           //Takes the addresss of b


Pointer Initialization and Assignment

void main()
{
 int i = 42, j = 911;
 int *iptr = &i; //means initialize memory location iptr
 //...
 *iptr = j; //means assign to memory pointed at by iptr
 //...

}





Pointer Use and Declaration

#include <iostream>
using namespace
std;

void main()
{
     int i = 42,j = 911;
     int
*iptr = &i;

     cout << "i is " << i << "\n";
     cout <<
"&i is " << &i << "\n";
     cout <<
"iptr is " << iptr << "\n";
     cout <<
"*iptr is " << *iptr << "\n";
     *iptr = j;
     cout <<
"i is " << i << "\n";
     cout <<
"&i is " << &i << "\n";
     cout <<
"iptr is " << iptr << "\n";
     cout <<
"*iptr is " << *iptr << "\n"
;
}

/* OUTPUT
i is 42
&i is 0012FF60
iptr is 0012FF60
*iptr is 42
i is 911
&i is 0012FF60
iptr is 0012FF60
*iptr is 911
*/



Why Are Pointers So Dangerous?

Pointers are easily redirected, or set to null,  causing the objects they referred to, to become abandoned (Memory Leak).

Pointers get redirected but mistakenly used as though they still refer to the original object.

Pointers can be incremented as though they are arrays (even when they don't refer to an array) and hence access out of bounds memory.

One possible solution: references.

 



Reference variables are aliases for objects they refer to  (they are hidden pointers) .
General Form: Type & reference = item;
The reference variable must be initialized to an existing item
The reference variable is used just like the variable it aliases (auto-dereferencing pointer)

// examples
int x = 10, y = 3;
int& rx = x;
int& ry = y;


 



//Sample program using reference variables

#include <iostream>
using namespace
std;

void main() {
    
int x = 10, y = 15;
    
int & rx = x;
    
int & ry = y;
 

     cout << "x is " << x << "\n";
     cout <<
"y is " << y << "\n";
     cout <<
"rx is " << rx << "\n";
     cout <<
"ry is " << ry << "\n";
    

     cout << "---------------\n";
     cout <<
"x + y is " << x+y << "\n";
     cout <<
"x + ry is " << x+ry << "\n";
     cout <<
"rx + ry is " << rx+ry << "\n";
    

     cout << "---------------\n";
     cout <<
"x is " << x << "\n";
     rx = 42;
     cout <<
"x is " << x << "\n"
;
}

/* OUTPUT
x is 10
y is 15
rx is 10
ry is 15
---------------
x + y is 25
x + ry is 25
rx + ry is 25
---------------
x is 10
x is 42
*/



In addition to passed by value and pass by pointer,  C++ provides another mechanism -passing by reference

A reference implicitly creates a pointer which is hidden from the programmer .

A reference can be used like the thing it references (no pointer manipulation required).

But remember, unlike a pass-by-value, pass by reference can alter the original object being referred to.



Pass By Value Example

#include <iostream>
using namespace
std;

int neg(int i){  //arg declared as value    
   
return -i;  //value returned
}

void main() {
  
int x;
   x = 10;
   cout <<
"x= " << x << "\n";
   x = neg(x);
//value passed to function
  
cout << "x= " << x << "\n"
;
}

/* OUTPUT
x= 10
x= -10
*/



Pass By Pointer Example

#include <iostream>
using namespace
std;

void neg(int *i){  //pointer argument   
    *i = -(*i); 
//used as pointers, no return
}

void main() {
  
int x;
   x = 10;
   cout <<
"x= " << x << "\n";
   neg(&x);
//value passed to function
  
cout << "x= " << x << "\n"
;
}

/* OUTPUT
x= 10
x= -10
*/

 



Pass By Reference  Example

#include <iostream>
using namespace
std;

void neg(int &i){  //reference argument   
    i = -i; 
//used like an object, not an address
}

void main() {
  
int x;
   x = 10;
   cout <<
"x= " << x << "\n";
   neg(x);
//looks just like pass by value in the client code
  
cout << "x= " << x << "\n"
;
}

/* OUTPUT
x= 10
x= -10
*/




Method Returns

Example 1, no chaining --methods return void

#include <iostream>
using namespace
std;

class BankAccount{
public
:

BankAccount (char * customer, float opening_balance = 0.0) {
   owner = customer; balance = opening_balance; }
  
float getBalance() {return balance; }
  
void deposit(float amount) {balance += amount;}
  
void withdraw(float
amount) {balance -= amount;}

private:
  
char * owner;
  
float
balance;

};

void main() {
   BankAccount b(
"Lou", 1000);
   b.deposit(500);
   b.withdraw(700);
   b.withdraw(450);
   cout <<
"balance= " << b.getBalance() << "\n";
//OUTPUTS 350

}




Example 2, Methods Returning by Value

#include <iostream>
using namespace
std;

class BankAccount{
public
:

BankAccount (char * customer, float opening_balance = 0.0) {
   owner = customer; balance = opening_balance; }
  
float getBalance() {return
balance; }

BankAccount deposit(float amount) {
   balance += amount;
return *this;}

BankAccount withdraw(
float amount) {
   balance -= amount;
return *this
;}

private:
  
char * owner;
  
float
balance;
};

void main() {
   BankAccount b(
"Lou", 1000);
   b.deposit(500).withdraw(700).withdraw(450);
   cout <<
"balance= " << b.getBalance() << "\n"; //OUTPUTS 1500 ---What went wrong?
}




Example 3, Methods Returning by Reference

#include <iostream>
using namespace
std;

class BankAccount{
public
:

BankAccount (char * customer, float opening_balance = 0.0) {
   owner = customer; balance = opening_balance; }
  
float getBalance() {return
balance; }

BankAccount & deposit(float amount) {
   balance += amount;
return *this;}

BankAccount & withdraw(
float amount) {
   balance -= amount;
return *this
;}

private:
  
char * owner;
  
float
balance;
};

void main() {
   BankAccount b(
"Lou", 1000);
   b.deposit(500).withdraw(700).withdraw(450);
   cout <<
"balance= " << b.getBalance() << "\n"; //OUTPUTS 350
}




Example 4, Methods Returning by Pointers

#include <iostream>
using namespace
std;

class BankAccount{
public
:

BankAccount (char * customer, float opening_balance = 0.0) {
   owner = customer; balance = opening_balance; }
  
float getBalance() {return
balance; }

BankAccount * deposit(float amount) {
   balance += amount;
return this;}

BankAccount * withdraw(
float amount) {
   balance -= amount;
return this
;}

private:
  
char * owner;
  
float
balance;
};

void main() {
   BankAccount b(
"Lou", 1000);
   b.deposit(500)->withdraw(700)->withdraw(450);
   cout <<
"balance= " << b.getBalance() << "\n"; //OUTPUTS 350
}