引用是一个别名。建立引用时,使用另外一个对象(目标)的名称来初始化它,今后之后该引用就像是目标的另外一个名称,对引用执行的任何操做实际上针对的就是目标。ios
有些书上说引用就是指针,这不正确。虽然引用经常是使用指针实现的,可是只有编译器开发人员关心这一点,做为程序员,必须区分这两种概念。c++
指针是存储另外一个对象的地址的变量,而引用时对象的别名。程序员
要建立引用,须要指定目标对象的类型、引用运算符(&)和引用名。函数
程序清单12.1 Reference.cpp指针
#include <iostream> int main() { int intOne; int &rSomeRef = intOne; intOne = 5; std::cout << "intOne: " << intOne << std::endl; std::cout << "rSomeRef: " << rSomeRef << std::endl; rSomeRef = 7; std::cout << "intOne: " << intOne << std::endl; std::cout << "rSomeRef: " << rSomeRef << std::endl; return 0; }
若是请求返回引用的地址,就将返回它指向的目标的地址。这是引用的特征:他们是目标的别名。code
程序清单12.2 Reference2.cpp对象
#include <iostream> int main() { int intOne; int &rSomeRef = intOne; intOne = 5; std::cout << "intOne: " << intOne << std::endl; std::cout << "rSomeRef: " << rSomeRef << std::endl; std::cout << "&intOne: " << &intOne << std::endl; std::cout << "&rSomeRef: " << &rSomeRef << std::endl; return 0; }
一般,使用引用时,不将地址运算符用于它,而像使用目标变量那样使用引用。blog
程序清单12.3 Assignment.cpp接口
#include <iostream> int main() { int intOne; int &rSomeRef = intOne; intOne = 5; std::cout << "intOne:\t" << intOne << std::endl; std::cout << "rSomeRef:\t" << rSomeRef << std::endl; std::cout << "&intOne:\t" << &intOne << std::endl; std::cout << "&rSomeRef:\t" << &rSomeRef << std::endl; int intTwo = 8; rSomeRef = intTwo; std::cout << "\nintOne:\t" << intOne << std::endl; std::cout << "intTwo:\t" << intTwo << std::endl; std::cout << "rSomeRef:\t" << rSomeRef << std::endl; std::cout << "&intOne:\t" << &intOne << std::endl; std::cout << "&intTwo:\t" << &intTwo << std::endl; std::cout << "&rSomeRef:\t" << &rSomeRef << std::endl; return 0; }
由于对象也是一种变量,因此可引用任何对象,包括用户定义的对象。能够像使用对象那样使用对象的引用:访问成员数据和成员函数时,使用类成员访问运算符(.)与内置类型的引用同样,指向对象的引用也是对象的别名。ci
指针未初始化或被删除时,应将其赋为nullptr,但引用不同,引用不能为空,让引用指向空对象的程序是非法的。
前面知道了函数的两个局限性:参数按值传递;return语句只能返回一个值。
经过将值按引用传递给函数,可消除这两种局限性。在c++中,按引用传递时经过两种方式完成的:使用指针和使用引用。他们的语法不一样,但效果相同:不是在函数做用域内建立备份(也就是否是值拷贝),而是将原始对象传递给函数。
程序清单12.4 ValuePasser.cpp
#include <iostream> void swap(int x, int y); int main() { int x = 5, y = 10; std::cout << "Main. Before swap,x: " << x << " y: " << y << std::endl; swap(x, y); std::cout << "Main. Before swap,x: " << x << " y: " << y << std::endl; return 0; } void swap(int x, int y) { int temp; std::cout << "Swap. Before swap,x: " << x << " y: " << y << std::endl; temp = x; x = y; y = temp; std::cout << "Swap. After swap,x: " << x << " y: " << y << std::endl; }
main()中的值都没变,可见值拷贝并不能改变原参的值
使用指针实现swap()
程序清单12.5 PointerSwap.cpp
#include <iostream> void swap(int *x, int *y); int main() { int x = 5, y = 10; std::cout << "Main. Before swap,x: " << x << " y: " << y << std::endl; swap(&x, &y);//将地址做为参数传递 std::cout << "Main. Before swap,x: " << x << " y: " << y << std::endl; return 0; } void swap(int *px, int *py)//参数声明为指针 { int temp; std::cout << "Swap. Before swap,*px: " << *px << " *py: " << *py << std::endl; temp = *px; *px = *py; *py = temp; std::cout << "Swap. After swap,*px: " << *px << " *py: " << *py << std::endl; }
使用引用实现swap()
c++的目标之一时,避免函数的调用者操心函数的工做原理,而将注意力放在函数的功能和返回值上。传递指针将负担转嫁给了调用方,而这种负担本来不该该由调用方来承担:调用方必须知道将要交换的对象的地址传入。
明白引用语法的负担应由函数的实现方承担。为此,可以使用引用。
程序清单12.6 ReferenceSwap.cpp
#include <iostream> void swap(int &x, int &y); int main() { int x = 5, y = 10; std::cout << "Main. Before swap,x: " << x << " y: " << y << std::endl; swap(x, y); std::cout << "Main. Before swap,x: " << x << " y: " << y << std::endl; return 0; } void swap(int &rx, int &ry)//参数声明为引用 { int temp; std::cout << "Swap. Before swap,rx: " << rx << " ry: " << ry << std::endl; temp = rx; rx = ry; ry = temp; std::cout << "Swap. After swap,rx: " << rx << " ry: " << ry << std::endl; }
可见两种方式(指针传地址、引用传原参)达到的效果是同样的,可是引用传递中,调用方只需传递变量,且在函数内部,须要使用的特殊符号减小了,下降了程序的复杂性。引用将常规变量方便而易于使用的特色和指针的强大融为一体。
函数原型的另外一个重要用途:经过查看原型中声明的参数(函数原型一般放在头文件中),程序员知道swap()的参数是按指针仍是引用传递的,从而将正确调用他们。
在c++中,类的使用者(其余类中使用该类的函数)依赖于头文件来获悉须要的全部信息。头文件至关于类或函数的接口,而实际实现对使用者是隐藏的。这让程序员可以将主要精力放在要解决的问题上,而使用类或函数时无需关心它是如何实现的。
(哦,这说和没说是同样的,仍是只能return一个东西)
一种解决办法是将多个对象按引用传入函数,而后在函数中将正确的值赋给这些对象。因为按引用传递让函数可以修改原始对象,所以这至关于让函数可以返回多项信息。这种函数未使用函数的返回值,可将其(指返回值)用于报告错误。
另外一种办法是使用指针或引用来实现。
程序清单12.7 ReturnPointer.cpp
#include <iostream> short factor(int, int *, int *); int main() { int number, squared, cubed; short error; std::cout << "Enter a number(0 - 20): "; std::cin >> number; error = factor(number, &squared, &cubed); if (!error) { std::cout << "number: " << number << "\n"; std::cout << "square: " << squared << "\n"; std::cout << "cubed: " << cubed << "\n"; } else std::cout << "Error encountered!!\n"; return 0; } short factor(int n, int *pSquared, int *pCubed) { short vaule = 0; if (n > 20) { vaule = 1; } else { *pSquared = n * n; *pCubed = n * n * n; vaule = 0; } return vaule; }
按引用返回值
虽然程序ReturnPointer.cpp可行,可是若是使用引用而不是指针,将更容易理解和维护。
程序清单12.8 ReturnReference.cpp
#include <iostream> enum ERR_CODE//枚举 { SUCCESS, ERROR }; ERR_CODE factor(int, int &, int &); int main() { int number, squared, cubed; ERR_CODE result; std::cout << "Enter a number(0 - 20): "; std::cin >> number; result = factor(number, squared, cubed); if (result == SUCCESS) { std::cout << "number: " << number << "\n"; std::cout << "square: " << squared << "\n"; std::cout << "cubed: " << cubed << "\n"; } else std::cout << "Error encountered!!\n"; return 0; } ERR_CODE factor(int n, int &pSquared, int &pCubed) { short vaule = 0; if (n > 20) { return ERROR; } else { pSquared = n * n; pCubed = n * n * n; return SUCCESS; } }