引用類型的函數參數
向函數傳遞引用而非大型對象的效率通常更高。 這使編譯器能夠在保持已用于訪問對象的語法的同時傳遞對象的地址。 請考慮以下使用了 Date 結構的示例:
// reference_type_function_arguments.cppstruct 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 = 7y = 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 namespaceusing 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 pointerint 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。