本文翻译自:https://docs.microsoft.com/en-us/cpp/cpp/reference-type-function-arguments?view=vs-2019
函数的引用类型参数
通常情况下相对于传递较大的对象,传递引用给函数更高效。传递引用给函数允许编译器传递对象的地址给函数的同时,保留用于直接访问该对象的syntax(应该指的是对象名称?)。This allows the compiler to pass the address of the object while maintaining the syntax that would have been used to access the object.
// reference_type_function_arguments.cpp #include <iostream> struct Date { short Month; short Day; short Year; }; // Create a date of the form DDDYYYY (day of year, year) // from a Date. long DateOfYear( Date& date ) { static int cDaysInMonth[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; long dateOfYear = 0; // Add in days for months already elapsed. for ( int i = 0; i < date.Month - 1; ++i ) dateOfYear += cDaysInMonth[i]; // Add in days for this month. dateOfYear += date.Day; // Check for leap year. if ( date.Month > 2 && (( date.Year % 100 != 0 || date.Year % 400 == 0 ) && date.Year % 4 == 0 )) dateOfYear++; // Add in year. dateOfYear *= 10000; dateOfYear += date.Year; return dateOfYear; } int main() { Date date{ 8, 27, 2018 }; long dateOfYear = DateOfYear(date); std::cout << dateOfYear << std::endl; }
上述例子展示了通过引用传递的结构体对象的成员是通过成员选择运算符(. member-selection operator)访问的,而不是指针的成员选择操作符->。
虽然通过引用传递的参数遵循非指针类型的语法,引用保留了指针的一个重要特性:除非被声明为const,它们都是可修改的。上面例子中并没有修改传入参数,因此应修改为:
long DateOfYear( const Date& date );
这样就保证了函数DateOfYear不会修改传入的参数。
Any function prototyped as taking a reference type can accept an object of the same type in its place because there is a standard conversion from typename to typename&.任何参数是引用类型的函数可以直接接受一个同类型的对象,因为存在从类型到类型引用的标准转换(也就是说:如果一个函数的参数是引用,你可以将一个对象直接传递给它)。
函数返回引用类型
函数可以被声明为返回一个引用类型,有两个原因这样做:
- 函数返回的对象足够大,返回引用比返回一个copy更有效;
- 函数类型必须是左值,The type of the function must be an l-value.
- 当调用函数返回时,函数返回的对象并不会被销毁(go out of scope)
与通过引用传递大的对象给函数会更高效类似,从函数中返回大对象的引用也会更高效。从函数中返回对象的引用消除了在函数返回前将对象拷贝到一个临时内存位置的必要性。
当函数被认为是左值是返回引用类型的对象也很有帮助。Reference-return types can also be useful when the function must evaluate to an l-value. Most overloaded operators fall into this category, particularly the assignment operator.
例子:
// 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() << " " << "y = " << ThePoint.y() << " "; }
输出:
x = 7 y = 9
注意:函数x y被声明为返回引用类型。这两个函数可以出现在赋值操作符=的左右两侧。
注意,在main函数中,ThePoint对象没有被销毁(remains in scope),因此它的成员仍然有且且可以安全的访问。
声明引用类型必须包含初始化器(initializer),除了以下几种情况:
- explicit extern declaration
- 类成员的声明
- 声明在一个类的内部
- 声明为一个函数的参数,或者函数的返回类型
返回局部变量的地址应注意 Caution returning address of local
如果你声明一个局部范围内的对象,则当函数离开该返回后对象会被销毁。如果函数返回指向该对象的引用,则使用该引用可能会导致运行时访问冲突(an access violation at runtime)。
// C4172 means Don’t do this!!! Foo& GetFoo() { Foo f; ... return f; } // f is destroyed here
编译器会提示警告:warning C4172: returning address of local variable or temporary
.在简单程序中很容易发现这样的错误,但是随着程序代码的增加这样的错误很难调试,使用时应注意。
指向指针的引用
指向指针的引用的声明与指向对象的引用的声明基本相同。指向指针的引用是一个可修改的值,其用法与普通的指针类似。指向指针的指针使用**来指向最终对象,而使用指向指针的引用的话在编写代码时更方便些。