详解C++编程中向函数传递引用参数的用法

 更新时间:2016年01月25日 16:09:42   投稿:goldensun  
这篇文章主要介绍了详解C++编程中向函数传递引用参数的用法,包括使函数返回引用类型以及对指针的引用,需要的朋友可以参考下

引用类型的函数参数
向函数传递引用而非大型对象的效率通常更高。 这使编译器能够在保持已用于访问对象的语法的同时传递对象的地址。 请考虑以下使用了 Date 结构的示例:

// 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()
{
}

前面的代码显示通过引用传递的结构的成员是通过成员选择运算符 (.) 访问的,而不是通过指针成员选择运算符 (–>) 访问的。
尽管作为引用类型传递的参数遵循了非指针类型的语法,但它们仍然保留了指针类型的一个重要特征:除非被声明为 const,否则它们是可以修改的。 由于上述代码的目的不是修改对象 GDate,因此更合适的函数原型是:

long JulianFromGregorian( const Date& GDate );

此原型将确保函数 JulianFromGregorian 不会更改其参数。
任何其原型采用引用类型的函数都能接受其所在位置的相同类型的对象,因为存在从 typename 到 typename& 的标准转换。

引用类型函数返回
可将函数声明为返回引用类型。 做出此类声明原因有:

  • 返回的信息是一个返回引用比返回副本更有效的足够大的对象。
  • 函数的类型必须为左值。
  • 引用的对象在函数返回时不会超出范围。

就像通过引用传递大型对象 to 函数或返回大型对象 from 函数可能更有效。 引用返回协议使得不必在返回前将对象复制到临时位置。

当函数的计算结果必须为左值时,引用返回类型也可能很有用。 大多数重载运算符属于此类别,尤其是赋值运算符。 重载运算符在重载运算符中有述。
示例
请考虑 Point 示例:

// 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";
}

输出

 x = 7
y = 9

请注意,函数x 和 y 被声明为返回引用类型。 这些函数可在赋值语句的每一端上使用。
另请注意在 main 中,ThePoint 对象停留在范围中,因此其引用成员仍处于活动状态,可以安全地访问。
除以下情况之外,引用类型的声明必须包含初始值设定项:

  • 显式 extern 声明
  • 类成员的声明
  • 类中的声明
  • 函数的参数或函数的返回类型的声明

返回局部变量地址时的注意事项

如果在局部范围中声明某个对象,则该对象会在函数返回时销毁。 如果函数返回对该对象的引用,则当调用方尝试使用 null 引用时,该引用可能会在运行时导致访问冲突。

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

编译器会在这种情况下发出警告:警告 C4172: 返回局部变量或临时变量的地址。 在简单程序中,如果调用方在覆盖内存位置之前访问引用,则有时可能不会发生访问冲突。 这纯属运气。 请注意该警告。

对指针的引用
声明对指针的引用的方式与声明对对象的引用差不多。声明对指针的引用将生成一个可像常规指针一样使用的可修改值。
以下代码示例演示了使用指向指针的指针与使用对指针的引用之间的差异。
函数 Add1 和 Add2 在功能上是等效的(虽然它们的调用方式不同)。二者的差异在于,Add1 使用双间接寻址,而 Add2 利用了对指针的引用的便利性。

// 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 );
  }
}

用法:Refptr [1 | 2]

其中:
1 使用双间接寻址
2 使用对指针的引用。输入来自 stdin。

相关文章

  • c++显式栈实现递归介绍

    c++显式栈实现递归介绍

    大家好,本篇文章主要讲的是c++显式栈实现递归介绍,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下
    2022-01-01
  • C语言圣诞树的实现示例

    C语言圣诞树的实现示例

    本篇主要介绍了C语言圣诞树的实现示例,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-12-12
  • C语言关键字const和指针的结合使用

    C语言关键字const和指针的结合使用

    这篇文章主要介绍了C语言关键字const和指针的结合,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-02-02
  • 浅谈int8_t int64_t size_t ssize_t的相关问题(详解)

    浅谈int8_t int64_t size_t ssize_t的相关问题(详解)

    下面小编就为大家带来一篇浅谈int8_t int64_t size_t ssize_t的相关问题(详解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-03-03
  • 带你了解C++中vector的用法

    带你了解C++中vector的用法

    大家好,本篇文章主要讲的是带你了解C++中vector的用法,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下
    2022-01-01
  • c++中string类成员函数c_str()的用法

    c++中string类成员函数c_str()的用法

    c_str()函数返回一个指向正规c字符串的指针,内容和string类的本身对象是一样的,通过string类的c_str()函数能够把string对象转换成c中的字符串的样式
    2013-09-09
  • C++程序函数的重载和函数模板示例代码

    C++程序函数的重载和函数模板示例代码

    C++允许在同一作用域中用同一函数名定义多个函数,这些函数的参数个数和参数类型不相同,这些同名的函数用来实现不同的功能,这就是函数的重载,这篇文章主要介绍了C++程序函数的重载和函数模板,需要的朋友可以参考下
    2024-03-03
  • C++事件处理中__event与__raise关键字的用法讲解

    C++事件处理中__event与__raise关键字的用法讲解

    这篇文章主要介绍了C++事件处理中__event与__raise关键字的用法,是C++入门学习中的基础知识,需要的朋友可以参考下
    2016-01-01
  • C++ tuple元组的基本用法(总结)

    C++ tuple元组的基本用法(总结)

    这篇文章主要介绍了C++ tuple元组的基本用法(总结),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • 基于c语言知识点的补遗介绍

    基于c语言知识点的补遗介绍

    本篇文章是对c语言知识点的一些补遗进行详细的分析介绍,需要的朋友参考下
    2013-05-05

最新评论