引用:相当于起了一个外号,但还是代表它本身。
- 引用必须初始化
- 变量可以有多个引用
- 引用只能引用一个变量
引用和指针的不同点:
- 引用概念上定义一个变量的别名,指针存储一个变量地址。
- 引用在定义时必须初始化,指针没有要求
- 引用在初始化时引用一个实体后,就不能在引用其他实体,而指针可以在任何时候指向任何一个同类型实体
- 没有NULL引用,但又NULL指针
- 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
- 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
- 有多级指针,但没有多级引用
- 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
- 引用比指针使用起来相对更安全
引用与赋值的区别在于:
引用与变量使用同一个空间地址。
int& ra = a;
赋值则是从新开辟一个空间。
int copy = a;
代码示例:
void test1()
{
int a = 10;
//ra是一个引用:是a的别名,指向和a相同的实体
//引用必须初始化
int& ra = a;
//变量有多个引用
int& ra1 = a;
int& ra2 = a;
//引用只能引用一个变量
a = 30;
int b = 100;
//不是修改引用的指向,而是把b的内容赋给ra1所指向的实体(此时a=100)
ra1 = b;
//常引用:引用指向的实体不能修改
const int a = 10;
const int& ra = a;
double d = 2.5;
//d会先给到一个整型的临时变量,临时变量具有常性
const int& rd = d;
}
函数传参
函数传参的两种写法:
一种是使用指针的概念:传地址给函数,函数接收之后,内部进行解引用赋值。
另一种是变量引用的概念:传变量给函数,函数进行引用,内部进行修改时还是修改的变量本身,而不是函数内部的临时变量。
void Swap(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
void Swap2(int& a, int& b)
{
int tmp = a;
a = b;
b = tmp;
}
void testSwap()
{
int a = 19;
int b = 2;
Swap(&a, &b);
Swap2(a, b);
}
引用做返回值
引用做返回值,返回值的生命周期需要大于函数的生命周期
否则在函数再次调用时会发生空间地址重新应用,造成值的丢失。
//变量c是在函数内部创建,其生命周期等于函数生命周期:在函数结束后会进行空间释放,重新调用函数时会重新使用该空间
int& add2(int& a, int& b)
{
int c = a + b;
return c;
}
//该函数返回的a的引用,生命周期大于函数的生命周期
int& add(int& a, int& b)
{
return a += b;
}
void test2()
{
int a = 1;
int b = 2;
int& ra_2 = add(a, b);
add(a, b);
cout << a << b << endl;
cout << ra_2 << endl;
}
传值所用的时间远远大于传引用时间
因为传值时需要开辟空间,而传引用则不需要
struct A
{
//四万个字节大小
int a[10000];
};
A globalA;
void fun1(A a)
{}
void fun2(A& a)
{}
void test3()
{
int n;
cout << "循环次数:" << endl;
cin >> n;
size_t begin = clock();
for (int i = 0; i < n; ++i)
fun1(globalA);
size_t end = clock();
cout << "传值time:" << end - begin << endl;
begin = clock();
for (int i = 0; i < n; ++i)
fun2(globalA);
end = clock();
cout << "传引用time:" << end - begin << endl;
}