详解C++编程中向函数传递引用参数的用法
引用类型的函数参数
向函数传递引用而非大型对象的效率通常更高。这使编译器能够在保持已用于访问对象的语法的同时传递对象的地址。请考虑以下使用了Date结构的示例:
//reference_type_function_arguments.cpp structDate { shortDayOfWeek; shortMonth; shortDay; shortYear; }; //CreateaJuliandateoftheformDDDYYYY //fromaGregoriandate. longJulianFromGregorian(Date&GDate) { staticintcDaysInMonth[]={ 31,28,31,30,31,30,31,31,30,31,30,31 }; longJDate=0; //Addindaysformonthsalreadyelapsed. for(inti=0;i<GDate.Month-1;++i) JDate+=cDaysInMonth[i]; //Addindaysforthismonth. JDate+=GDate.Day; //Checkforleapyear. if(GDate.Year%100!=0&&GDate.Year%4==0) JDate++; //Addinyear. JDate*=10000; JDate+=GDate.Year; returnJDate; } intmain() { }
前面的代码显示通过引用传递的结构的成员是通过成员选择运算符(.)访问的,而不是通过指针成员选择运算符(–>)访问的。
尽管作为引用类型传递的参数遵循了非指针类型的语法,但它们仍然保留了指针类型的一个重要特征:除非被声明为const,否则它们是可以修改的。由于上述代码的目的不是修改对象GDate,因此更合适的函数原型是:
longJulianFromGregorian(constDate&GDate);
此原型将确保函数JulianFromGregorian不会更改其参数。
任何其原型采用引用类型的函数都能接受其所在位置的相同类型的对象,因为存在从typename到typename&的标准转换。
引用类型函数返回
可将函数声明为返回引用类型。做出此类声明原因有:
- 返回的信息是一个返回引用比返回副本更有效的足够大的对象。
- 函数的类型必须为左值。
- 引用的对象在函数返回时不会超出范围。
就像通过引用传递大型对象to函数或返回大型对象from函数可能更有效。引用返回协议使得不必在返回前将对象复制到临时位置。
当函数的计算结果必须为左值时,引用返回类型也可能很有用。大多数重载运算符属于此类别,尤其是赋值运算符。重载运算符在重载运算符中有述。
示例
请考虑Point示例:
//refType_function_returns.cpp //compilewith:/EHsc #include<iostream> usingnamespacestd; classPoint { public: //Define"accessor"functionsas //referencetypes. unsigned&x(); unsigned&y(); private: //Notethatthesearedeclaredatclassscope: unsignedobj_x; unsignedobj_y; }; unsigned&Point::x() { returnobj_x; } unsigned&Point::y() { returnobj_y; } intmain() { PointThePoint; //Usex()andy()asl-values. ThePoint.x()=7; ThePoint.y()=9; //Usex()andy()asr-values. cout<<"x="<<ThePoint.x()<<"\n" <<"y="<<ThePoint.y()<<"\n"; }
输出
x=7 y=9
请注意,函数x和y被声明为返回引用类型。这些函数可在赋值语句的每一端上使用。
另请注意在main中,ThePoint对象停留在范围中,因此其引用成员仍处于活动状态,可以安全地访问。
除以下情况之外,引用类型的声明必须包含初始值设定项:
- 显式extern声明
- 类成员的声明
- 类中的声明
- 函数的参数或函数的返回类型的声明
返回局部变量地址时的注意事项
如果在局部范围中声明某个对象,则该对象会在函数返回时销毁。如果函数返回对该对象的引用,则当调用方尝试使用null引用时,该引用可能会在运行时导致访问冲突。
//C4172meansDon'tdothis!!! Foo&GetFoo() { Foof; ... returnf; }//fisdestroyedhere
编译器会在这种情况下发出警告:警告C4172:返回局部变量或临时变量的地址。在简单程序中,如果调用方在覆盖内存位置之前访问引用,则有时可能不会发生访问冲突。这纯属运气。请注意该警告。
对指针的引用
声明对指针的引用的方式与声明对对象的引用差不多。声明对指针的引用将生成一个可像常规指针一样使用的可修改值。
以下代码示例演示了使用指向指针的指针与使用对指针的引用之间的差异。
函数Add1和Add2在功能上是等效的(虽然它们的调用方式不同)。二者的差异在于,Add1使用双间接寻址,而Add2利用了对指针的引用的便利性。
//references_to_pointers.cpp //compilewith:/EHsc #include<iostream> #include<string> //STLnamespace usingnamespacestd; enum{ sizeOfBuffer=132 }; //Defineabinarytreestructure. structBTree{ char*szText; BTree*Left; BTree*Right; }; //Defineapointertotherootofthetree. BTree*btRoot=0; intAdd1(BTree**Root,char*szToAdd); intAdd2(BTree*&Root,char*szToAdd); voidPrintTree(BTree*btRoot); intmain(intargc,char*argv[]){ //Usagemessage if(argc<2){ cerr<<"Usage:Refptr[1|2]"<<"\n"; cerr<<"\nwhere:\n"; cerr<<"1usesdoubleindirection\n"; cerr<<"2usesareferencetoapointer.\n"; cerr<<"\nInputisfromstdin.\n"; return1; } char*szBuf=newchar[sizeOfBuffer]; if(szBuf==NULL){ cerr<<"Outofmemory!\n"; return-1; } //Readatextfilefromthestandardinputdeviceand //buildabinarytree. //while(!cin.eof()) { cin.get(szBuf,sizeOfBuffer,'\n'); cin.get(); if(strlen(szBuf)){ switch(*argv[1]){ //Method1:Usedoubleindirection. case'1': Add1(&btRoot,szBuf); break; //Method2:Usereferencetoapointer. case'2': Add2(btRoot,szBuf); break; default: cerr<<"Illegalvalue'" <<*argv[1] <<"'suppliedforaddmethod.\n" <<"Choose1or2.\n"; return-1; } } } //Displaythesortedlist. PrintTree(btRoot); } //PrintTree:Displaythebinarytreeinorder. voidPrintTree(BTree*MybtRoot){ //Traversetheleftbranchofthetreerecursively. if(btRoot->Left) PrintTree(btRoot->Left); //Printthecurrentnode. cout<<btRoot->szText<<"\n"; //Traversetherightbranchofthetreerecursively. if(btRoot->Right) PrintTree(btRoot->Right); } //Add1:Addanodetothebinarytree. //Usesdoubleindirection. intAdd1(BTree**Root,char*szToAdd){ if((*Root)==0){ (*Root)=newBTree; (*Root)->Left=0; (*Root)->Right=0; (*Root)->szText=newchar[strlen(szToAdd)+1]; strcpy_s((*Root)->szText,(strlen(szToAdd)+1),szToAdd); return1; } else{ if(strcmp((*Root)->szText,szToAdd)>0) returnAdd1(&((*Root)->Left),szToAdd); else returnAdd1(&((*Root)->Right),szToAdd); } } //Add2:Addanodetothebinarytree. //Usesreferencetopointer intAdd2(BTree*&Root,char*szToAdd){ if(Root==0){ Root=newBTree; Root->Left=0; Root->Right=0; Root->szText=newchar[strlen(szToAdd)+1]; strcpy_s(Root->szText,(strlen(szToAdd)+1),szToAdd); return1; } else{ if(strcmp(Root->szText,szToAdd)>0) returnAdd2(Root->Left,szToAdd); else returnAdd2(Root->Right,szToAdd); } }
用法:Refptr[1|2]
其中:
1使用双间接寻址
2使用对指针的引用。输入来自stdin。