引用类型的函数参数
向函数传递引用而非大型对象的效率通常更高。 这使编译器能够在保持已用于访问对象的语法的同时传递对象的地址。 请考虑以下使用了 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 对象停留在范围中,因此其引用成员仍处于活动状态,可以安全地访问。
除以下情况之外,引用类型的声明必须包含初始值设定项:
返回局部变量地址时的注意事项
如果在局部范围中声明某个对象,则该对象会在函数返回时销毁。 如果函数返回对该对象的引用,则当调用方尝试使用 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。
本文向大家介绍Python函数中参数是传递值还是引用详解,包括了Python函数中参数是传递值还是引用详解的使用技巧和注意事项,需要的朋友参考一下 在 C/C++ 中,传值和传引用是函数参数传递的两种方式,在Python中参数是如何传递的?回答这个问题前,不如先来看两段代码。 代码段1: 看了代码段1的同学可能会说参数是值传递。 代码段2: 看了代码段2,这时可能又有人会说,参数是传引用,那么问题
清单2.4中,向std::thread构造函数中的可调用对象,或函数传递一个参数很简单。需要注意的是,默认参数要拷贝到线程独立内存中,即使参数是引用的形式,也可以在新线程中进行访问。再来看一个例子: void f(int i, std::string const& s); std::thread t(f, 3, "hello"); 代码创建了一个调用f(3, "hello")的线程。注意,函数f需
本文向大家介绍C#函数式编程中的递归调用之尾递归详解,包括了C#函数式编程中的递归调用之尾递归详解的使用技巧和注意事项,需要的朋友参考一下 关于递归相信大家已经熟悉的不能再熟悉了,所以笔者在这里就不多费口舌,不懂的读者们可以在博客园中找到很多与之相关的博客。下面我们直接切入正题,开始介绍尾递归。 尾递归 普通递归和尾递归如果仅仅只是从代码的角度出发来看,我们可能发现不了他的特点,所以笔者利用两张堆
本文向大家介绍JavaScript函数参数的传递方式详解,包括了JavaScript函数参数的传递方式详解的使用技巧和注意事项,需要的朋友参考一下 JavaScript使用一个变量对象来追踪变量的生存期。基本类型值被直接保存在变量对象内;而引用类型值则作为一个指针保存在变量对象内,该指针指向实际对象在内存中的存储位置。 基本类型值的传递 向参数传递基本类型值时,被传递的值会被复制给一个局部变量(即
本文向大家介绍详解python函数传参是传值还是传引用,包括了详解python函数传参是传值还是传引用的使用技巧和注意事项,需要的朋友参考一下 首先还是应该科普下函数参数传递机制,传值和传引用是什么意思? 函数参数传递机制问题在本质上是调用函数(过程)和被调用函数(过程)在调用发生时进行通信的方法问题。基本的参数传递机制有两种:值传递和引用传递。 值传递(passl-by-value)过程中,被调
我正在尝试将参数传递给作为参数传递的函数指针。 代码: 我得到了这个错误: 类型"void"的参数与类型"void(*)(wchar_t*,wchar_t*)"的参数不兼容 如何解决此问题以完成我想要实现的目标? 编辑:对不起,不清楚。我实际上试图完成的是将函数注入子进程并传递两个参数(wchar_t*、wchar_t*),以便我可以使用它们。但主函数可以是void或int argc、char**