一、概念
引用不是定义一个新变量,而是给已存在的变量取一个别名,编译器不会为引用变量开辟内存空间,而是和其引用实体共用同一块内存
二、特性
- 引用必须和其引用实体是同种类型
void TestRef()
{
double a = 12.34;
int &ra = a; /* 错误:将类型为‘int&’的引用初始化为类型为‘double’的表达式无效 */
}
- 引用在定义时必须初始化
void TestRef()
{
int &ra; /* 错误:‘ra’声明为引用却未被初始化 */
}
-
引用一旦初始化引用一个实体后,再不能引用其他实体
-
一个变量可以有多个引用
void TestRef()
{
int a = 10;
int &ra = a;
int &rra = a;
printf("%p %p %p\n", &a, &ra, &rra); /* 输出结果一致 */
}
三、常引用
void TestConstRef()
{
const int a = 10;
/*
* int &ra = a;
* 错误:将类型为‘int&’的引用初始化为类型为‘const int’的表达式无效
*/
const int &ra = a;
printf("%p %p\n", &a, &ra); /* 输出结果一致 */
/*
* int &b = 10;
* 错误:用类型为‘int’的右值初始化类型为‘int&’的非常量引用无效
*/
const int &b = 10;
int c = 10;
const int &rc = c;
printf("%p %p\n", &c, &rc); /* 输出结果一致 */
double d = 12.34;
const int &rd = d;
printf("%p %p\n", &d, &rd); /* 注意:输出结果不一致 */
}
四、使用场景
1、参数
void Swap(int &left, int &right)
{
int temp = left;
left = right
right = temp;
}
2、返回值
#include <iostream>
using namespace std;
int &TestRef(int &r)
{
r += 10;
return r;
}
/*
* 反面案例
* 注意:如果函数返回时,离开函数作用域后,其栈上空间已经还给系统,因此不能用栈上的空间作为引用类型返回,
* 如果以引用类型返回,返回值的生命周期必须不受函数的限制,即比函数生命周期长
*/
int &Add(int a, int b)
{
int c = a + b;
return c;
}
int main()
{
int &r = Add(1, 2);
Add(3, 4);
count << "Add(1,2) is " << r << endl;
return 0;
}
五、引用和指针的区别
当参数或者返回值类型非常大时,用值作为参数或者返回值类型,性能是非常低下的,为什么?
传参需要参数压栈,返回值可参考C/C++函数返回值问题
那指针和引用作为参数和返回值类型的性能呢?
1、引用和指针作为参数类型的性能比较
#include <iostream>
#include <time.h>
using namespace std;
struct TestType
{
int a[10000];
};
void TestFunc1(TestType *a)
{}
void TestFunc2(TestType &a)
{}
void TestRefAndPtr()
{
TestType a;
size_t begin1 = clock();
for (size_t i = 0; i < 1000000; ++i)
TestFunc1(&a);
size_t end1 = clock();
size_t begin2 = clock();
for (size_t i = 0; i < 1000000; ++i)
TestFunc2(a);
size_t end2 = clock();
cout << "TestFunc1(int *)-time: " << end1 - begin1 << endl;
cout << "TestFunc1(int &)-time: " << end2 - begin2 << endl;
cout << endl;
}
/* 多运行几次 */
int main()
{
for (int i = 0; i < 10; i++)
TestRefAndPtr();
return 0;
}
2、引用和指针作为返回值类型的性能比较
#include <iostream>
#include <time.h>
using namespace std;
struct TestType
{
int a[10000];
};
TestType a;
TestType *TestFunc1()
{
return &a;
}
TestType &TestFunc2()
{
return a;
}
void TestRefAndPtr()
{
size_t begin1 = clock();
for (size_t i = 0; i < 1000000; ++i)
TestFunc1();
size_t end1 = clock();
size_t begin2 = clock();
for (size_t i = 0; i < 1000000; ++i)
TestFunc2();
size_t end2 = clock();
cout << "TestType *TestFunc1()-time: " << end1 - begin1 << endl;
cout << "TestType &TestFunc1()-time: " << end2 - begin2 << endl;
cout << endl;
}
/* 多运行几次 */
int main()
{
for (int i = 0; i < 10; i++)
TestRefAndPtr();
return 0;
}
通过上述比较,发现引用和指针在作为传参以及返回值类型上性能几乎相同
3、引用和指针的区别
在语法概念上,引用就是一个别名,没有独立空间,和其引用实体共用同一块空间,但实际在底层实现上是有空间的,因为引用是按照指针方式实现的
/*
* 可以看下汇编代码
* g++ -S -fverbose-asm -g test.cc -o test.s
* as -alhnd test.s
*/
int main()
{
int a = 10;
int &ra = a;
ra = 20;
int *pa = &a;
*pa = 20;
return 0;
}
引用和指针的不同点:
- 有 void *,没有 void &
- 有 NULL 指针,没有 NULL 引用
- 有多级指针,没有多级引用
- 引用在定义时必须初始化,指针没有要求
- 引用一旦初始化引用一个实体后,再不能引用其他实体,而指针可以在任何时候指向任何一个同类型实体
- 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
- 引用自加即引用的变量增加 1,指针自加即指针向后偏移一个指针类型的大小
#include <stdio.h>
int main()
{
int a = 10;
int &ra = a;
int b = 20;
int *pb = &b;
printf("a: %d, ra: %d\n", a, ra);
ra++;
printf("ra: %d, a: %d\n", ra, a);
printf("b: %d, pb: %llu\n", b, pb);
(*pb)++;
printf("pb: %llu, b: %d\n", pb, b);
pb++;
printf("pb: %llu, b: %d\n", pb, b);
return 0;
}
- sizeof 引用为引用类型的大小,sizeof 指针始终是地址空间所占字节数(32 位平台下占 4 字节,64 位平台下占 8 字节)
#include <stdio.h>
int main()
{
int a;
int &ra = a;
int *pa = &a;
printf("sizeof(ra): %d, sizeof(pa): %d\n"
, sizeof(ra), sizeof(pa)); /* sizeof(ra): 4, sizeof(pa): 8 */
return 0;
}
- 引用比指针使用起来相对更安全
3万+

被折叠的 条评论
为什么被折叠?



