详解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。

(0)

相关推荐

  • C++浅拷贝与深拷贝及引用计数分析

    C++浅拷贝与深拷贝及引用计数分析 在C++开发中,经常遇到的一个问题就是与指针相关的内存管理问题,稍有不慎,就会造成内存泄露.内存破坏等严重的问题.不像Java一样,没有指针这个概念,所以也就不必担心与指针相关的一系列问题,但C++不同,从C语言沿袭下来的指针是其一大特点,我们常常要使用new/delete来动态管理内存,那么问题来了,特别是伴随着C++的继承机制,如野指针.无效指针使用.内存泄露.double free.堆碎片等等,这些问题就像地雷一样,一不小心就会踩那么几颗. 先来谈一下C

  • 简单谈谈C++中指针与引用的区别

    指针与引用是C++中两个很重要的概念,它们功能看过去很相似,就是都是间接引用某个对象,那么我们应该在什么时候使用指针,什么时候使用引用呢,下面请允许我慢慢道来: 1.永远不要使用一个指向空值的引用.一个引用必须始终指向某个对象,所以当你确定使用一个变量指向某个对象时,但是这个对象在某些时间可能指向控制,这时候你就必须把变量声明为指针类型,而不是引用!当你确定这个变量始终指向某个对象是,这时候你就可以把变量声明为引用类型. char *str=0; //设置指针为空值 char &s=*str;

  • 详谈C++引用&和指针在作为形参时的区别

    int n; int &m = n; 在C++中,多了一个C语言没有的引用声明符&,如上,m就是n的引用,简单的说m就是n的别名,两者在内存中占同样的位置,不对m开辟新的内存空间,对m的任何操作,对n来说是一样的. 对于引用,有以下三条规则: (1)引用被创建的同时必须被初始化(指针则可以在任何时候被初始化). (2)不能有NULL 引用,引用必须与合法的存储单元关联(指针则可以是NULL). (3)一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象). 假如在一个函数中

  • C++常量详解一(常量指针与常量引用的初始化)

    1.常量 1.1.常量的初始化: const对象一旦创建后其值就不能再改变,所以const对象必须初始化.这里我们要注意一点,像const int *p和const int &r都并不是const对象.因为const int *p只是表示不能通过p改变p所指的对象的值,p的值是可以变的,所以p可以不用初始化.至于r ,引用本身就不是对象,所以r也并不是const对象,r之所以一定初始化,是因为引用必须初始化.对于以上内容,你也可以理解为底层const 修饰的并不是const对象,还要注意像con

  • 详解C++11中的右值引用与移动语义

    C++11的一个最主要的特性就是可以移动而非拷贝对象的能力.很多情况都会发生对象的拷贝,有时对象拷贝后就立即销毁,在这些情况下,移动而非拷贝对象会大幅度提升性能. 右值与右值引用 为了支持移动操作,新标准引入了一种新的引用类型--右值引用,就是必须绑定到右值的引用.我们通过&&而不是&来获得右值引用.右值引用一个重要的特性就是只能绑定到将要销毁的对象. 左值和右值是表达式的属性,一些表达式生成或要求左值,而另一些则生成或要求右值.一般而言,一个左值表达式表示的是一个对象的身份,而右

  • 深入理解c++指针的指针和指针的引用

    展示一下使用指针的指针和指针的引用修改传递给方法的指针,以便更好的使用它.(这里说的指针的指针不是一个二维数组) 为什么需要使用它们 当我们把一个指针做为参数传一个方法时,其实是把指针的复本传递给了方法,也可以说传递指针是指针的值传递. 如果我们在方法内部修改指针会出现问题,在方法里做修改只是修改的指针的copy而不是指针本身,原来的指针还保留着原来 的值.我们用下边的代码说明一下问题: int m_value = 1; void func(int *p) { p = &m_value; } i

  • 举例剖析C++中引用的本质及引用作函数参数的使用

    引用的意义与本质 1)引用作为其它变量的别名而存在,因此在一些场合可以代替指针 2)引用相对于指针来说具有更好的可读性和实用性 引用本质思考: 思考.C++编译器背后做了什么工作? #include <iostream> using namespace std; int main() { int a = 10; // 单独定义的引用时,必须初始化:说明很像一个常量 int &b = a; // b是a的别名 b = 11; cout << "b--->&qu

  • C++ 中引用与指针的区别实例详解

    C++ 中引用与指针的区别实例详解 引用是从C++才引入的,在C中不存在.为了搞清楚引用的概念,得先搞明白变量的定义及引用与变量的区别,变量的要素一共有两个:名称与空间. 引用不是变量,它仅仅是变量的别名,没有自己独立的空间,它只符合变量的"名称"这个要素,而"空间"这个要素并不满足.换句话说,引用需要与它所引用的变量共享同一个内存空间,对引用所做的改变实际上是对所引用的变量做出修改.并且引用在定义的时候就必须被初始化.     参数传递的类型及相关要点: 1 按值

  • 详解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

  • 详解C++编程中对于函数的基本使用

    形式参数和实际参数 在调用函数时,大多数情况下,函数是带参数的.主调函数和被调用函数之间有数据传递关系.前面已提到:在定义函数时函数名后面括号中的变量名称为形式参数(formal parameter,简称形参),在主调函数中调用一个函数时,函数名后面括号中的参数(可以是一个表达式)称为实际参数(actual parameter,简称实参). [例]调用函数时的数据传递. #include <iostream> using namespace std; int max(int x,int y)

  • jQuery position() 函数详解以及jQuery中position函数的应用

    position()函数用于返回当前匹配元素相对于其被定位的祖辈元素的偏移,也就是相对于被定位的祖辈元素的坐标.该函数只对可见元素有效. 所谓"被定位的元素",就是元素的CSS position属性值为absolute.relative或fixed(只要不是默认的static即可). 该函数返回一个坐标对象,该对象有一个left属性和top属性.属性值均为数字,它们都以像素(px)为单位. 与offset()不同的是:position()返回的是相对于被定位的祖辈元素的坐标,offse

  • 详解向scrapy中的spider传递参数的几种方法(2种)

    有时需要根据项目的实际需求向spider传递参数以控制spider的行为,比如说,根据用户提交的url来控制spider爬取的网站.在这种情况下,可以使用两种方法向spider传递参数. 第一种方法,在命令行用crawl控制spider爬取的时候,加上-a选项,例如: scrapy crawl myspider -a category=electronics 然后在spider里这样写: import scrapy class MySpider(scrapy.Spider): name = 'm

  • 详解python requests中的post请求的参数问题

    问题:最新在爬取某站点的时候,发现在post请求当中,参数构造正确却获取不到数据,索性将post的参数urlencode之后放到post请求的url后面变成get请求,结果成功获取到数据,对此展开疑问. 1.http请求中Form Data和Request Playload的区别: Ajax post请求中常用的两种参数形式:form data 和 request payload get请求的时候,我们的参数直接反映在url里面,为key1=value1&key2=value2形式,如果是pos

  • 详解C语言中的函数、数组与指针

    1.函数:当程序很小的时候,我们可以使用一个main函数就能搞定,但当程序变大的时候,就超出了人的大脑承受范围,逻辑不清了,这时候就需要把一个大程序分成许多小的模块来组织,于是就出现了函数概念:   函数是C语言代码的基本组成部分,它是一个小的模块,整个程序由很多个功能独立的模块(函数)组成.这就是程序设计的基本分化方法: (1) 写一个函数的关键: 函数定义:函数的定义是这个函数的实现,函数定义中包含了函数体,函数体中的代码段决定了这个函数的功能: 函数声明:函数声明也称函数原型声明,函数的原

  • 详解WordPress开发中wp_title()函数的用法

    wp_title 函数在 WordPress 中是用来显示文章.页面.分类等等等等标题的一个函数,但在首页索引,该函数将不显示任何的东西.该函数在 WordPress 官方主题中一直被使用,但目前很多定制的主题中这个函数总是为忽视. 函数意义详解 wp_title 函数用来显示页面的标题,如在文章页面,则显示文章标题:在分类页面,则显示分类名称,但在首页索引,该函数将不显示任何的东西. 有点像 WordPress 中的 get_the_title 和 single_cat_title()这两个函

  • 详解Python编程中基本的数学计算使用

    数 在 Python 中,对数的规定比较简单,基本在小学数学水平即可理解. 那么,做为零基础学习这,也就从计算小学数学题目开始吧.因为从这里开始,数学的基础知识列位肯定过关了. >>> 3 3 >>> 3333333333333333333333333333333333333333 3333333333333333333333333333333333333333L >>> 3.222222 3.222222 上面显示的是在交互模式下,如果输入 3,就显

  • 详解Java编程中final,finalize,finally的区别

    final: final可以让你控制你的成员.方法或者是一个类是否可被覆写或继承等功能,这些特点使final在Java中拥有了一个不可或缺的地位,也是学习Java时必须要知道和掌握的关键字之一. final成员 当你在类中定义变量时,在其前面加上final关键字,那便是说,这个变量一旦被初始化便不可改变,这里不可改变的意思对基本类型来说是其值不可变,而对于对象变量来说其引用不可再变.其初始化可以在两个地方,一是其定义处,二是在构造函数中,两者只能选其一. 下面程序很简单的演示了final的常规用

  • 详解Swift编程中的常量和变量

    常量 常量指的是程序无法在其执行期间改变的固定值. 常量可以是任何像整型常量,浮点常量,字符常量或字符串的基本数据类型.也可以是枚举常量. 这些常量和常规变量处理一样,只是它们的值不能在定义后进行修改. 声明常量 使用常量时,则必须使用关键字 let 声明它们如下: 复制代码 代码如下: let constantName = <initial value> 下面是一个简单的例子来说明如何在 Swift 中声明一个常量: 复制代码 代码如下: import Cocoa let constA =

随机推荐