CPSC 427a: Object-Oriented Programming
Michael J. Fischer
Functions and Methods
Call by value
Like C, C++ passes explicit parameters by value.
Call by pointer
Like C, pointer values (which I call reference values) are the things that can be stored in pointer variables.
Also like C, references values can be passed as arguments to functions having corresponding pointer parameters.
Call by reference
C++ has a new kind of parameter called a reference parameter.
I/O uses reference parameters
How should one choose the parameter type?
Parameters are used for two main purposes:
Sending data to a function: call by value For sending data to a function, call by value copies the data whereas call by pointer or reference copies only an address.
Sending data to a function: call by reference or pointer
Call by reference or pointer allows the caller’s data to be changed.
Use const to protect the caller’s data from inadvertane change.
Ex: int f( const int& x ) or int g( const int* xp ).
Prefer call by reference to call by pointer for input parameters.
Ex: f( 234 ) works but g( &234 ) does not.
Reason: 234 is not a variable and hence can not be the target of a pointer.
(The reason f( 234 ) does work is a bit subtle and will be explained later.)
Receiving data from a function
An output parameter is expected to be changed by the function.
Both call by reference and call by pointer work.
Call by reference is generally preferred since it avoids the need for the
caller to place an ampersand in front of the output variable.
Declaration: int f( int& x ) or int g( int* xp ).
Call: f( result ) or g( &result ).
The implicit argument
Every call to a class member function has an implicit argument, which is the object written before the dot in the function call.
this
The implicit argument is passed by pointer.
In the call ex.advance(3), the implicit argument is ex, and a pointer to ex is passed to advance().
The implicit argument can be referenced directly from within a member function using the keyword this.
Within the definition of advance(), count and this->count are synonymous.
Simple variables
L-values and R-values Programming language designers have long been bothered by the asymmetry of assignment.
x = 3 is a legal assignment statement. |
3 = x is not legal. |
Expressions are treated differently depending on whether they appear on the left or right sides of an assignment statement.
Something that can appear on the left is called an L-value.
Something that can appear on the right is called an R-value.
Intuitively, an L-value is the address of a storage location – some place where a value can be stored.
An R-value is a thing that can be placed in a storage location.
R-values are sometimes called pure data values.
Simple variable declaration
The declaration int x = 3; says several things:
The L-value of x is the address of the storage location of x.
The R-value of x is the object of type int that is stored in x.
Simple assignment
The assignment statement x = 3; means the following:
Automatic dereferencing
Given
Consider
x = y;
This is processed as before, except what does it mean to get an R-value from y?
Whenever an L-value is presented and an R-value is needed, automatic deferencing occurs.
This means to go the storage location specified by the presented L-value (y) and get its R-value (4).
Then the assignment takes place as before.
Pointers
Pointer values
Pointer creation
Pointer variables
Variables into which pointers can be stored are called (not surprisingly) pointer variables.
A pointer variable is no different from any other variable except for the types of values that can be stored in it.
Just as we often conflate “integer” and “integer variable”, it is easy to confuse “pointer” with “pointer variable”.
Pointer assignment
Pointers can be assigned to pointer variables.
Following a pointer
To follow a pointer means to obtain the L-value it encapsulates.
Pointer example
p = &x; | // p points to x. |
*p = 5; | // Now x==5. |
q = p; | // p and q both point to x. |
*q = *p + 1; | // Now x==6. |
Common mistake – dangling pointer
| |
*r = x+y; | // What’s wrong here? |
Pointer declaration syntax
A word of warning
int x, y; is shorthand for int x; int y; but
int* p, q; is not same as int* p, int* q.
Rather, it means int* p; int q;.
For this reason, many authors put the * next to the variable instead of with the type name.
Spacing around the star doesn’t matter, but logically it belongs with the type.
References
Reference types Recall: Given int x, two types are associated with x: an L-value (the reference to x) and an R-value (the type of its values).
C++ exposes this distinction through reference types and declarators.
A reference type is any type T followed by &, i.e., T&.
A reference type is the internal type of an L-value.
Example: Given int x, the name x is bound to an L-value of type int&, whereas the values stored in x have type int
This generalizes to arbitrary types T: If an L-value stores values of type T, then the type of the L-value is T&.
Reference declarators
The syntax T& can be used to declare names, but its meaning is not what one might expect.
int x = 3; | // Ordinary int variable |
int& y = x; | // y is an alias for x |
y = 4; | // Now x == 4. |
The declaration must include an initializer.
The meaning of int& y = x; is that y becomes a name for the L-value x.
Since x is simply the name of an L-value, the effect is to make y an alias for x.
For this to work, the L-value type (int&) of x must match the type declarator (int&) for y, as above.
Use of named references
Named references can be used just like any other variable.
One application is to give names to otherwise unnamed storage locations.
Reference parameters
References are mainly useful for function parameters and return values.
When used to declare a function parameter, they provide call-by-reference semantics.
int f( int& x ){…}
Within the body of f, x is an alias for the actual parameter, which must be the L-value of an int location.
Reference return values
Functions can also return references.
This code returns a reference to the smaller of x and y and then sets that variable to their sum.
Custom subscripting
Suppose you would like to use 1-based arrays instead of C++’s 0-based arrays.
We can define our own subscript function so that sub(a, k) returns the L-value of array element a[k-1].
sub(a,k) can be used on either the left or right side of an assignment statement, just like the built-in subscript operator.
Constant references
Constant reference types allow the naming of pure R-values.
const double& pi = 3.1415926535897932384626433832795;
Actually, this is little different from
const double pi = 3.1415926535897932384626433832795;
In both cases, the pure R-value is placed in a read-only variable, and pi is bound to its L-value.
Comparison of reference and pointer