Variable Types

Reference types
- Alternative names for variables/objects with which it has been initialized
- Sometimes referred to as an alias for another variable/object
- Sometimes referred to as a special type of pointer
- All operations applied to the reference act on the object to which it refers
- Defined by following the type with the address-of operator (&)
- References, like constants, must be initialized upon declaration
- After initialized, references cannot be made to reference another object

 

#include <iostream>

using namespace std;

main()
{
    int val = 10;
    int &refVal = val;
    int &refVal2;       // Error: uninitialized (must refer to something)

    // Operations on referenced variables
    cout << “val: “ << val << endl;
    refVal += 2;
    cout << “val: “ << val << endl;

    // Can assign reference values
    int ii = refVal;

    // Changing the original referenced value
    int val2 = 2;
    refVal = val2;
    cout << “val: “ << val << endl;
}

Output
val: 10
val: 12
val: 2

 

 

Reference Parameters
- Primary use of references are as function arguments
- Especially useful with class methods
- One of two ways in C++ for performing call-by-reference

- References now give us 3 different ways to pass arguments to a function
        1) call by value
        2) call by reference - with pointer arguments
        3) call by reference - with reference types

- Using reference types in function changes original values
- Function call looks like a call by value, but operates as call by reference
- Sometimes referred to as automatically dereferenced pointers

#include <iostream>

using namespace std;

 

// call by reference with reference type

void add( int & );

 

main()
{
    int val = 10;

    cout << “val: “ << val << endl;

 

    // looks like a call by value
    add( val );


    cout << “val: “ << val << endl;
}

 

// Function changes original value

void add( int &t )
{
    t += 2;
}

Output
val: 10
val: 12

- Recall that arguments are copied onto the stack when functions are called

- If a class object is passed by value, stack memory must be allocated for all state variables in the class

- For classes containing a large amount of state variables, this operation can be costly (memory, processor)

- Passing parameters by reference (with reference types or pointers) avoids memory overhead

- Recall that only the address is copied onto to the stack when passing arguments by reference

- Call by reference, however, can weaken security in a program....why?...
- Because called functions can corrupt the caller's data (by changing the original)
- So...how can we get the cost benefit of call by reference yet assure security?

- The answer is to use references with a new keyword, const

 

 

 

const keyword
- Call by reference (with reference types or pointers) allows elements to be modified
- What if you wanted to pass by reference but prevent any changes?
- Keyword
const provides way to assure that arguments cannot be changed
- Arguments with
const keyword can only be used (read) in the function, but not modified (write)

- This provides an efficient way to pass large class objects to a function that is only using its values (not modifying them)

- In this way, we get the cost benefits of call by reference while assuring that the values will not be changed

- Consider the following example with a standard function

void add( const int & );      // constant reference type argument

void main()
{
    int val = 10;
    add( val );               // call by reference (with reference type)
}

void add( const int &t )
{
    // Can use the argument
    int ai = t;

    // Error - cannot modify the argument
    t += 2;
}

- To assure security, this program will not compile
- Since the argument is passed as a constant reference, the function is not allowed to modify its value
- The compiler reports the following error

t.C:6: error: `main' must return `int'

t.C: In function `void add(const int&)':

t.C:16: error: assignment of read-only reference `t'

 

 

- Constant references can also be used with class member functions (methods)

 

#include <iostream>

using namespace std;

class BBB
{
private:
    int val;

public:
    BBB( int v=0 ) { val = v; }
    void Add( const float &x )      // constant reference argument
    {
       val += x;                    // only using the argument
    }
    void Print() { cout << “val: “ << endl; }
};

main()
{
    BBB b( 4 );
    b.Print();
    int myVal = 2;
    b.Add( myVal );
    b.Print();
}

Output
val: 4
val: 6

 

Conventions
- When arguments are to be used and not modified, it is recommended to declare with const to assure security
- Use call by reference with pointers when passing arguments to be modified
- Use call by reference with
const reference types when passing large non-modifiable arguments
- Use call by value when passing small non-modifiable arguments

 

this pointer
- Object member functions have a hidden argument
- The argument is passed implicitly by the compiler and never seen
- The argument is the address of the object itself, or pointer, named this
- This pointer can be used within a class's member functions to refer to back to the object itself
- We can return this pointer to chain function calls as in the example below
- We can also use it to refer to state variable names (as in the constructor below)

class LimitedCounter
{
private:
    int count, limit;

public:
    LimitedCounter(int count=0, int limit=100)
    {
         this->count = count;
         this->limit = limit;
    }

    LimitedCounter *inc(int amount=1)
    {
         if (count + amount < limit)
           count += amount;
         else
           count = limit;

         return this;
    }

    int getCount() { return count; }
};

main()
{
    LimitedCounter *lc = new LimitedCounter();
    lc->inc(5)->inc(10)->inc(20);
    printf( "Count: %d\n", lc->getCount() );
}

  

String class

- C++ provides a useful class, string, to process strings as part of its standard library

- Class provides for strings to be used as normal built-in types
- Can copy, assign, and compare strings as fundamental types
- Simply use operators, (=, ==, +) to work with strings

#include <iostream>
using namespace std;

main()
{
  // Construction
  string s1( "cat" ), s2, s3;

  // Assignment
  s2 = s1;
  s3.assign( s1 );
  cout << "s1: " << s1 << endl;
  cout << "s2: " << s2 << endl;
  cout << "s3: " << s3 << endl;

  // Modification and access
  s2[0] = s3[2] = 'r';
  cout << "s2: " << s2 << endl;
  cout << "s3: " << s3 << endl;
  int len = s3.length();
  for( int x = 0; x < len; ++x )
    cout << s3.at( x );
  cout << endl;

  // Concatenation
  string s4( s1 + "apult" );
  cout << s4 << endl;
  s3 += "pet";
  cout << s3 << endl;
  s1.append( "acomb" );
  cout << s1 << endl;

  // Comparison
  string t1( "compare" );
  string t2( "compare" );
  if( t1 == t2 )
    cout << "strings match!" << endl;

}

Output
s1: cat
s2: cat
s3: cat
s2: rat
s3: car
car
catapult
carpet
catacomb
strings match!