Function arguments for reference types
passing a reference to a function rather than a large object is generally more efficient. This enables the compiler to pass the address of an object while maintaining the syntax that has been used to access the object. Consider the following examples that use the DATE structure:
Reference_type_function_arguments.cpp
struct Date
{short
dayofweek;
Short Month;
Short day;
Short year;
};
Create a Julian date of the form dddyyyy
//from a Gregorian date.
Long Julianfromgregorian (date& gdate)
{
static int cdaysinmonth[] = {
31, 28, 31, 30, 31, 30, 31, 31, (a
);
Long jdate = 0;
ADD in months already elapsed.
for (int i = 0; i < gdate.month-1 ++i)
JDate + = Cdaysinmonth[i];
ADD in month.
JDate + = Gdate.day;
Check for leap year.
if (gdate.year%!= 0 && gdate.year% 4 = 0)
jdate++;
Add in year.
JDate *= 10000;
JDate + = Gdate.year;
return jdate;
}
int main ()
{
}
The preceding code shows that the members of a struct passed by reference are accessed through the member selection operator (.), not through the pointer member selection operator (–>).
Although parameters passed as reference types follow the syntax of a non pointer type, they still retain an important feature of the pointer type: they can be modified unless they are declared as Const. Since the purpose of the above code is not to modify object gdate, a more appropriate function prototype is:
Long Julianfromgregorian (const date& gdate);
This prototype will ensure that the function Julianfromgregorian does not change its arguments.
Any function whose prototype takes a reference type can accept objects of the same type in which they are located, because there is a standard conversion from TypeName to typename&.
Reference type function returns
a function can be declared as a return reference type. The reasons for making such statements are:
- The returned information is an object that returns a reference that is more efficient than the return copy.
- The type of the function must be a left value.
- The referenced object does not go out of scope when the function returns.
Just as passing a large object to function by reference or returning a large object from function may be more efficient. The reference return protocol makes it unnecessary to copy objects to a temporary location before returning.
Reference return types can also be useful when the function evaluates to a left value. Most overloaded operators fall into this category, especially assignment operators. Overloaded operators are described in overloaded operators.
Example
Consider the point example:
Reftype_function_returns.cpp
//compile with:/EHsc
#include <iostream>
using namespace std;
Class Point
{public
:
//Define "accessor" functions as
//reference types.
unsigned& x ();
unsigned& y ();
Private:
//Note this are declared at class scope:
unsigned obj_x;
unsigned obj_y;
};
unsigned& point:: X ()
{return
obj_x;
}
unsigned& point:: Y ()
{return
obj_y;
}
int main ()
{point
thepoint;
Use X () and Y () as l-values.
Thepoint.x () = 7;
Thepoint.y () = 9;
Use X () and Y () as r-values.
cout << "x =" << thepoint.x () << "\ n"
<< "y =" << thepoint.y () << "\ n";
Output
Note that functions x and Y are declared as return reference types. These functions can be used at each end of an assignment statement.
Also note that in main, the Thepoint object stays in scope, so its reference member is still active and can be accessed securely.
A declaration of a reference type must contain an initializer, except for the following:
- An explicit extern declaration
- Declaration of Class members
- declaration in a class
- Declaration of the return type of a function's argument or function
Considerations when returning a local variable address
If an object is declared in a local scope, the object is destroyed when the function returns. If the function returns a reference to the object, the reference may cause an access violation at run time when the caller attempts to use a null reference.
C4172 means Don ' t do this!!!
foo& Getfoo ()
{
Foo F;
...
return f;
} F is destroyed
The compiler warns in this case: Warning C4172: Returns the address of a local variable or a temporary variable. In a simple program, an access violation may sometimes not occur if the caller accesses the reference before overwriting the memory location. It's sheer luck. Please note this warning.
A reference to a pointer
declaring a reference to a pointer is similar to declaring a reference to an object. Declaring a reference to a pointer produces a modifiable value that can be used like a regular pointer.
The following code example demonstrates the difference between using a pointer pointing to a pointer and using a reference to a pointer.
Functions Add1 and ADD2 are functionally equivalent (although they are invoked differently). The difference is that ADD1 uses dual indirection, while ADD2 utilizes the convenience of referencing pointers.
References_to_pointers.cpp//compile with:/EHsc #include <iostream> #include <string>//STL namespace
using namespace Std;
enum {sizeofbuffer = 132};
Define a binary tree structure.
struct Btree {char *sztext;
Btree *left;
Btree *right;
};
Define a pointer to the root of the tree.
Btree *btroot = 0;
int Add1 (btree **root, char *sztoadd);
int Add2 (btree*& Root, char *sztoadd);
void Printtree (btree* btroot); int main (int argc, char *argv[]) {//Usage message if (ARGC < 2) {cerr << "usage:refptr [1 |
2] "<<";
Cerr << "\nwhere:\n";
Cerr << "1 uses double indirection\n";
Cerr << "2 uses a reference to a pointer.\n";
Cerr << "\ninput is from stdin.\n";
return 1;
} char *szbuf = new Char[sizeofbuffer];
if (szbuf = = NULL) {cerr << "out of memory!\n";
return-1;
}//Read a text file from the standard input device and//builds a binary tree. //while (!cin.eof ()) {cin.get (szbuf, sizeOfBuffer, ' \ n ');
Cin.get ();
if (strlen (szbuf)) {switch (*argv[1]) {//method 1:use double indirection.
Case ' 1 ': Add1 (&btroot, szbuf);
Break
Method 2:use reference to a pointer.
Case ' 2 ': Add2 (Btroot, szbuf);
Break Default:cerr << "Illegal value '" << *argv[1] << "supplied for add method.\
N "<<" Choose 1 or 2.\n;
return-1;
}}//Display the sorted list.
Printtree (Btroot);
}//Printtree:display the binary tree in order.
void Printtree (btree* mybtroot) {//Traverse the left branch to the tree recursively.
if (btroot->left) Printtree (btroot->left);
Print the current node.
cout << btroot->sztext << "\ n";
Traverse the right branch to the tree recursively. if (btroot->right) Printtree (btroot->
right);
}//Add1:add a node to the binary tree.
Uses double indirection.
int Add1 (btree **root, char *sztoadd) {if ((*root) = = 0) {(*root) = new Btree;
(*root)->left = 0;
(*root)->right = 0;
(*root)->sztext = new Char[strlen (Sztoadd) + 1];
strcpy_s ((*root)->sztext, (strlen (Sztoadd) + 1), sztoadd);
return 1;
else {if (strcmp (*root)->sztext, Sztoadd) > 0) return Add1 ((*root)->left), Sztoadd);
else return Add1 (& (*root)->right), Sztoadd);
}//Add2:add a node to the binary tree.
Uses reference to pointer int Add2 (btree*& root, char *sztoadd) {if (root = = 0) {root = new Btree;
Root->left = 0;
root->right = 0;
Root->sztext = new Char[strlen (Sztoadd) + 1];
strcpy_s (Root->sztext, (strlen (Sztoadd) + 1), sztoadd);
return 1; else {if (strcmp (Root->sztext, Sztoadd) > 0) return Add2 (Root->left, Sztoadd);
else return Add2 (Root->right, Sztoadd);
}
}
Usage: refptr [1 | 2]
which
1 Using dual indirection
2 use a reference to the pointer. Input from stdin.