Details the use of passing reference arguments to functions in C++ programming

  • 2020-05-07 20:13:23
  • OfStack

Function arguments of the reference type
is generally more efficient at passing references to functions rather than large objects. This enables the compiler to pass the object's address while maintaining the syntax that has been used to access the object. Consider the following example using 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, 30, 31, 30, 31
  };
long JDate = 0;
// Add in days for months already elapsed.
for ( int i = 0; i < GDate.Month - 1; ++i )
JDate += cDaysInMonth[i];
// Add in days for this month.
JDate += GDate.Day;

// Check for leap year.
if ( GDate.Year % 100 != 0 && GDate.Year % 4 == 0 )
JDate++;
// Add in year.
JDate *= 10000;
JDate += GDate.Year;

return JDate;
}

int main()
{
}

The previous code shows that the members of the structure passed by reference are accessed by the member selection operator (.), not by the pointer member selection operator (wok) > (visiting.
Although arguments passed as reference types follow the syntax of non-pointer types, they retain one important feature of pointer types: they can be modified unless they are declared as const. Since the purpose of the above code is not to modify the object GDate, a more appropriate function prototype is:


long JulianFromGregorian( const Date& GDate );

This prototype ensures that the function JulianFromGregorian does not change its parameters.
Any function whose prototype takes a reference type can accept the same type of object in its place, because there is a standard conversion from typename to typename&.

The reference type function returns
declares a function to return a reference type. Reasons for making such a statement include:

The information returned is a sufficiently large object that returns a reference more efficiently than a copy. The type of the function must be an lvalue. The referenced object does not go out of scope when the function returns.

Just as passing the large object to function by reference or returning the large object from function may be more efficient. The reference return protocol makes it unnecessary to copy an object to a temporary location before returning.

References to return types can also be useful when the result of a function's calculation must be lvalue. Most overloaded operators, especially assignment operators, fall into this category. Overloaded operators are described in overloaded operators.
The sample
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 that these 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";
}

The output


 x = 7
y = 9

Notice that the functions x and y are declared to return reference types. These functions can be used on each side of an assignment statement.
Note also that in main, the ThePoint object remains in scope, so its reference members are still active and can be accessed safely.
A declaration of a reference type must contain an initializer except:

Explicit extern declaration Declaration of class members Declaration in a class A declaration of the argument to a function or the return type of a function

Considerations when returns the address of a local variable

If an object is declared in a local scope, it is destroyed when the function returns. If the function returns a reference to the object, the reference may cause access conflicts at run time when the caller tries to use the null reference.


// C4172 means Don't do this!!!
Foo& GetFoo()
{
  Foo f;
  ...
  return f;
} // f is destroyed here

The compiler will issue a warning in this case: warning C4172: returns the address of a local or temporary variable. In a simple program, if the caller accesses the reference before overwriting the memory location, access conflicts may sometimes not occur. It was pure luck. Please heed the warning.

references to Pointers
declares references to Pointers in much the same way as it declares references to objects. Declaring a reference to a pointer generates a modifiable value that can be used like regular pointer 1.
The following code example demonstrates the difference between using a pointer to a pointer and using a reference to a pointer.
The functions Add1 and Add2 are functionally equivalent (although they are called differently). The difference between the two is that Add1 USES double indirection, while Add2 takes advantage of the convenience of a reference to a pointer.


// 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]" << "\n";
   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
  // build 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 of the tree recursively.
  if ( btRoot->Left )
   PrintTree( btRoot->Left );

  // Print the current node.
  cout << btRoot->szText << "\n";

  // Traverse the right branch of 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]

Among them:
1 use double indirection
Use a reference to the pointer. Input from stdin.


Related articles: