概念
定义:引用是已定义变量的别名。
分类:左值引用(&),右值引用(&&)。
语法:double& lref;
| double&& rref;
。
优点:节省时间和内存。
主要用途:作为参数的形参。引用变量用作参数,函数将使用原始数据,而不是其副本。
示例:
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
double x = 512.0;
double& r1 = x;
double& r2 = r1;
cout << "r1 = " << r1 << endl;
cout << "r2 = " << r2 << endl;
cout << &x << " " << &r1 << " " << &r2 << endl;
return 0;
}
// 输出:
// r1 = 512
// r2 = 512
// 0x61fe90 0x61fe90 0x61fe90
// 引用变量与被引用变量的地址相同
特点:一生只效忠一个变量。引用必须在声明的时候初始化,类似指针常量:int* const p = num;
。
示例:
int main()
{
double num1 = 512;
double* p = &num1;
double& r = *p;
double num2 = 1024;
p = &num2;
// 利用以上方式并不能改变引用变量r的指向
cout << "num1 = " << num1 << endl;
cout << "r = " << r << endl;
cout << "*p = " << *p << endl;
cout << "num2 = " << num2 << endl;
return 0;
}
// 输出:
// num1 = 512
// r = 512
// *p = 1024
// num2 = 1024
引用传递
引用经常被用作函数参数,这种方式叫作引用传递。
区别于值传递:
- 值传递:创建一个变量x,来接收实参的值。只能操作实参的副本;
- 引用传递:创建一个引用变量r,作为实参的别名。可直接对实参操作。
void swap(int& a, int& b)
{
a = a + b;
b = a - b;
a = a - b;
}
int main()
{
int a = 512, b = 1024;
swap(a, b);
cout << "a = " << a << endl;
cout << "b = " << b << endl;
return 0;
}
// 输出:
// a = 1024
// b = 512
但引用传递的使用是隐晦的:
// 声明
void swap(int a, int b); // 值传递
void swap(int& a, int& b); // 引用传递
void swap(int* a, int* b); // 指针传递
// 定义
...
// 调用
swap(a, b);
swap(a, b); // 隐晦(与值传递相同)
swap(&a, &b); // 明显
常量引用
引用容易引起原数据被改变。如果不希望原数据被改变,可考虑值传递或者常量引用。
double func(const double& r);
建议:如果基本数值类型,应采用值传递;当数据比较大时(类、结构),考虑引用传递。
一般地,引用参数只接收变量:
double func(double& rre)
{
return rre;
}
int main()
{
double num = 512;
double& r = num;
cout << func(r + 3) << endl; // (r + 3) 不是变量
return 0;
}
此时编译器会报错:
error: invalid initialization of non-const reference of type 'double&' from an rvalue of type 'double'
但const引用解决了这种尴尬:
double func(const double& rre)
{
return rre;
}
int main()
{
double num = 512;
double& r = num;
cout << func(r + 3) << endl;
return 0;
}
// 输出:
// 515
这是因为实参与引用参数不匹配,C++将生成临时变量(在函数调用期间存在),可打印两个变量的地址如下,确定它们并非同一变量。仅当参数为const引用时。
cout << &rre << endl; // 0x61fe88
cout << &r << endl; // 0x61fe90
出现临时变量的两种情况:
- 实参的类型正确,但不是左值;
- 实参的类型不正确,但可以转换为正确的类型。
double func(const double& rre)
{
cout << &rre << endl;
return rre;
}
int main()
{
int num = 512;
int& r = num;
cout << &r << endl;
func(r + 3);
return 0;
}
// 输出:
// 0x61fe8c
// 0x61fe90
左值:常规变量、const变量。
非左值:字面常量(字符串外,因为它们是其地址表示),包含多项的表达式。
const的好处
- 避免无意中修改数据;
- 使用const的函数能够处理const和非const实参,不使用的只能处理非const;
- const引用使函数能够正常生成并使用临时变量。
返回引用
返回引用的函数实际上就是被引用的变量的别名。因此使得以下语句语法正确:
double& func(double& rre)
{
return rre;
}
int main()
{
double num = 512;
func(num) = 1024; // 语法正确
cout << "num = " << num << endl;
return 0;
}
// 输出:
// num = 1024
如果想避免函数结果成为左值,可以加上const:
const double& func(double& rre) {...}
传统返回机制与值传递类似:结果被复制到一个临时位置,调用程序将使用这个值。
double m = sqrt(16.0); // 值4.0被复制到临时位置,然后被复制给m
返回引用则是:可直接使用结果。
double& func(xxx) {...}
double x = func(xxx) // 直接把func的结果赋值给x
常规返回类型是右值,不能通过地址访问。如字面值10.0,表达式x+y等。常规函数返回值之所以也是右值,是因为它的返回值位于临时内存单元中,运行到下一条语句时,它们可能不再存在。==需要注意的是:==即便函数返回的是全局变量或者static变量,传统返回机制也是copy一份临时变量,再通过这个临时变量赋值。
注意事项
在返回引用中额外需要注意:
- 避免返回函数终止时不再存在的内存单元的引用;
- 考虑返回一个作为参数传递给函数的引用。
引用用于类和对象
基类引用可以指向派生类对象。
总结
使用引用的两个主要原因:
- 能够修改调用函数中的数据对象;
- 通过传递引用,可以提高程序的运行速度。
使用传递的值而不作修改的函数:
- 如果数据对象很小,如内置数据类型或小型结构,用值传递;
- 如果数据对象时数组,则使用指针(因为这是唯一的选择),并将指针声明为指向const的指针;
- 如果数据对象是较大的结构,则使用const指针或const引用,提升程序的效率(时间和空间);
- 如果数据对象是类对象,则使用const引用。(类的设计语义常常要求使用引用,这是C++新增此特性的主要原因,因此,传递类对象参数的标准是引用传递。);
- 如果对象是内置数据类型,选择指针传递;
- 如果数据对象是数组,则只能使用指针;
- 如果数据对象是结构,则使用引用或指针;
- 如果数据对象是类对象,则使用引用。
不必严格按照以上标准。
还不快抢沙发