Declares a named variable as a reference, that is, an alias to an already-existing object or function. 来自: C++参考手册
先从一个简单的例子开始: 交换函数 swap() 。c++
// 代码片断01 void swap01(int a, int b) { int temp = a; a = b; b = temp; } // 代码片断02 void swap02(int *a, int *b) { int temp = *a; *a = *b; *b = temp; } int main(int argc, const char * argv[]) { int a= 10, b = 20; swap01(a, b); // 不会交换 swap02(&a, &b); // 会交换 return 0; }
上述代码彷佛不用解释都知道它所要说明的问题。彷佛与今天的主题无关,可是不着急,看下 代码片断02 。内部充斥着 *a
、*b
这样的操做,代码彷佛不够简洁。有没有什么方式可使其更简洁一些呢?有! 引用!!
。程序员
有了引用之后,代码是这样的:数组
void swap03(int &a, int &b) { int temp = a; a = b; b = temp; } int main(int argc, const char * argv[]) { int a= 10, b = 20; swap03(a, b); // 会交换 return 0; }
代码看起来和上面没有什么大的差异。&a
、&b
彷佛是!在取地址。实际上不是的。它表示的 a
是某个 int
类型变量的引用(或者说某个 int
类型变量的别名)。函数
那么如何建立一个 引用
呢?spa
int main(int argc, const char * argv[]) { int num = 10; int & ref = num; // 建立一个引用。 int & refref = ref; // 建立一个引用 return 0; }
如上,建立的是 num
的引用,名字叫 ret
。既然是引用,就是说,使用 num
和 ret
没有任何区别。 设计
没有任何区别是什么意思?意思就是说:num
和 ret
都是同一个内存地址的名称。举个小例子,如图:
指针
num: 周樟寿 ref: 周树人 refref: 鲁迅
只是名字变了,但它都是同一个内存!!code
说到这里,有没有发现其实引用是什么?像不像下面这样:对象
int &ref = num; // 等价于 int *ref = #
另外有一个须要注意的地方是: 引用一旦建立,便不能更改(只读)!!既然不能修改,那就说明必须在建立时就要给引用初始化!!!。blog
int one = 10; int two = 20; // 错误01(ref建立时就要被初始化) int &ref; ref = one; // 正确写法:int &ref = one; // 异常 02(引用一旦建立,便不能修改) int &ref = one; ref = two; // 这句话不会报错,可是它表达的意思不是让ref变成two的引用。而是将 two 的值,赋值给 ref。即,ref = 20;
至此,对引用有了一个基本的认识。总结一下:
基于以上的观点。咱们能够对引用有一个本质上的认识,即指针常量:
int & ref = num; // 等价于 int * const ref = #
引用难道只是为了简化
*a
这样的代码,使代码更简洁么?
不是的。引用来源于指针,但不一样于指针。
引用最主要的用途是:用做函数的参数(尤为是结构体或对象参数)!!
做为函数参数
struct Student { std::string id; std::string name; int age; }; void func01(Student student) { // do something } void func02(Student &student) { // do something }
【注意】 使用引用做为参数时,常加上 const
修饰。理由以下:
避免无心中修改被引用的数据,如:
int i = 5; int cube(int &a) { a *= a * a; return a; } int sum(int &a) { a = a + 5; return a; } int tmp1 = cube(i); // 输入时i= 5,输出时i = 125 int tmp2 = sum(i); // 输入时i = 125,输出时i = 130
可以处理 const
和 非const
实参,不然只能接受 非const
数据,如:
const int NUM = 100; int sum01(int &a) { // do something return 0; } int sum02(const int &a) { // do something return 0; } sum01(NUM);// ERROR!!!! sum02(NUM);// OK
做为函数返回值
返回值是引用的函数,实际上返回的是被引用变量的“别名”
有一个须要注意的地方是:不要返回临时变量。以下:
Student &clone(const Student &s) { Student student; // 局部变量,函数执行完成时会被销毁。 student.id = s.id; student.name = s.name; student.age = s.age; return student; }
如上,会返回 student。可是 student 是局部的临时变量,在 clone 函数执行完成后,将被销毁,致使程序出现异常。避免这种问题有一些作法:
返回一个做为参数传递给函数的引用。如:
Student &clone(const Student &s) { return s; }
用 new
来分配新的内存空间, 如:
Student &clone(const Student &s) { Student *student = new Student(); student->id = s.id; student->name = s.name; student->age = s.age; return *student; }
使用引用参数的主要缘由有两个:
- 程序员可以修改调用函数中的数据对象。
- (最主要)经过传递引用而不是整个数据对象(如:结构体和类对象这种数据对象较大的对象),能够提升程序的运行速度。
正如前面所说,引用来源于指针,但不一样于指针。甚至引用像是指针的 "语法糖" 。那么问题来了,何时使用引用,何时使用指针,何时使用按值传递呢???
对于使用传递的值而不做修改的函数
- 若是数据对象比较小(如:基本数据类型或者小型的结构),选择按值传递
- 若是数据对象是数组,则使用指针,由于这是惟一的选择,并将指针声明为指向
const
的指针- 若是数据对象是较大的结构,则使用指针或者引用,以提升程序的效率。这样能够节省复制结构所需的时间和空间
- 若是数据对象是类对象,则使用
const
修饰的引用。类设计的语义经常要求使用语义,这是C++新增这项特性的主要缘由。所以传递类对象参数的标准方式是按引用传递。对于修改调用函数中数据的函数
- 若是数据对象是基本数据类型,则使用指针。
- 若是数据对象是数组,则只能使用指针。
- 若是数据对象是结构,则使用指针或者引用。
- 若是数据对象是类对象,则使用引用。