引用
4_C++基础::引用
什么是引用?
引用本质上就是取别名
例如孙悟空、美猴王、齐天大圣……
例如铁牛、黑旋风、李逵……
这些都是引用,即别名
引用需要注意什么呢?
- 引用的实体在定义的时候必须被初始化
- 一个引用变量引用一个实体后,不得再引用其他实体
- 一个实体可以有多个变量引用
怎么引用呢?
类型名 & 引用变量名 = 引用实体;
ps:引用变量和引用实体必须是同一类型的!!!
int mian()
{
int a = 0;
int& b = a;//这里b就是a的引用
printf("%d %d",a,b);
return 0;
}
引用的作用
1、做参数
a、输出型参数
void swap(int& a, int& b)
{
int tmp = a;
a = b;
b = tmp;
}
int main()
{
int a = 1;
int b = 2;
printf("交换前:a=%d ,b=%d\n", a, b);
swap(a, b);
printf("交换后:a=%d ,b=%d\n", a, b);
return 0;
}
b、大对象传参提高效率(没有临时拷贝)
2、做返回值
a、输出型函数的返回对象
int& count(int num)
{
static int c = 0;
while(num >= 0)
{
c++;
num++;
}
return c;
}
int main()
{
std::cout << count(10) << std::endl;
return 0;
}
b、提高效率,减少拷贝
传值返回和传引用返回
顾名思义,传值返回就是将返回值拷贝返回,传引用返回就是返回变量的引用
怎么看待传值返回和传引用返回呢?
做返回对象,以下写法对吗?
int& add(int a, int b)
{
int c = 0;
cout << &c <<endl;
c = a + b;
return c;
}
int main()
{
int& a = add(1, 2);
cout << &a << " " << a << endl << endl;
int& b = add(2, 3);
cout << &b << " " << b << endl << endl;
cout << &a << " " << a << endl << endl;
return 0;
}
这个函数采用传引用返回:
在第二次打印a的时候就变成了随机值?但从都到尾我们所有用到的变量都是同一块区域,这里我们可以这样理解:
这也就是传引用的缺点。
但是传值返回呢?
这种的返回方式就是传值返回但是会涉及到两次拷贝的问题,那我们看看传值返回和传引用返回的效率怎么样?
传引用和传值传参
#include <time.h>
struct A { int a[10000]; };
void TestFunc1(A a) {}//传值传参
void TestFunc2(A& a) {}//传引用传参
void TestRefAndValue()
{
A a;
// 以值作为函数参数
size_t begin1 = clock();
for (size_t i = 0; i < 10000; ++i)
TestFunc1(a);
size_t end1 = clock();
// 以引用作为函数参数
size_t begin2 = clock();
for (size_t i = 0; i < 10000; ++i)
TestFunc2(a);
size_t end2 = clock();
// 分别计算两个函数运行结束后的时间
cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}
传值返回和传引用返回
#include <time.h>
struct A { int a[10000]; };
A a;
// 值返回
A TestFunc1() { return a; }
// 引用返回
A& TestFunc2() { return a; }
void TestReturnByRefOrValue()
{
// 以值作为函数的返回值类型
size_t begin1 = clock();
for (size_t i = 0; i < 100000; ++i)
TestFunc1();
size_t end1 = clock();
// 以引用作为函数的返回值类型
size_t begin2 = clock();
for (size_t i = 0; i < 100000; ++i)
TestFunc2();
size_t end2 = clock();
// 计算两个函数运算完成之后的时间
cout << "TestFunc1 time:" << end1 - begin1 << endl;
cout << "TestFunc2 time:" << end2 - begin2 << endl;
}
指针和引用
int main()
{
int a = 0;
int* b = &a;//指针
int& c = a;//引用
cout << "&a:" << &a << endl;
cout << "&b(指针):" << &b << endl;
cout << "&c(引用):" << &c << endl;
return 0;
}
在语法角度来说,指针是重新开辟了一块空间,引用是取别名,与原变量共用一块地址空间。
但看汇编看到指针和引用的比较,实际上是有空间的,引用的底层是用指针来实现的。
引用和指针的区别
- 引用概念上定义一个变量的别名,指针存储一个变量地址。
- 引用在定义时必须初始化,指针没有要求
- 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
- 没有NULL引用,但有NULL指针
- 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
- 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
- 有多级指针,但是没有多级引用
- 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
- 引用比指针使用起来相对更安全
权限的放大和缩小
首先,必须肯定的是
const
的引用只能是const int&;
权限放大
当然,int
引用可以利用const int
,这里把这种叫做权限的缩小
int main()
{
int a = 0;
const int& b = a;//权限缩小
return 0;
}
接下来看下面的例子
int main()
{
int a = 0;
double b = a;
double& rd = a;//不合法
const double& rrd = a;//合法
return 0;
}
为什么
double& rd = a;
不合法呢?仅仅是因为类型不同?
所以这里的本质原因是因为类型转化产生的临时变量具有常性,而直接采用double &
来引用常性的话会涉及到权限放大,所以不合法。
由此我们在函数传参的时候为了避免这种情况的出现,统一采用const
修饰来接收传来的形参
总结
引用相当于取别名,一个变量可以有多个引用,引用时必须被初始化且之后不能引用其他变量
传值返回和传引用返回的区别:
传值返回:会产生临时拷贝,影响效率
传引用返回:不会产生临时变量,但要注意在函数在结束时作用域销毁,会清理栈帧,这种情况下不要采取传值返回
指针和引用
引用的底层也是采取指针(其余总结见上)
权限的放大和缩小
为了避免权限放大,在函数形参采用
const
来进行统一修饰来接收传参