c++中的引用
一、引用
1、引用的概念:给变量取别名。
形式:原类型名& 别名 = 引用实体旧名;
2、特性:
①引用定义时必须初始化
②引用一旦初始化之后就不能再改变引用的指向
③不能引用NULL
④&再等号的左边为引用,在等号的右边为取地址
引用在定义时必须初始化。否则不知道引用的哪一个
#include <iostream>
using namespace std;
int main(){
int a=10;
int& ra;//编译时出错
int& r = a;
return 0;
}
引用一旦引用一个实体,再也不能引用其它实体。不然就是赋值。
#include <iostream>
using namespace std;
int main(){
int a=10;
int b=20;
int& ra=a;
ra =b;//赋值 //ra是前面引用a的,ra再次引用就是等于赋值了
return 0;
}
3.常引用
①一个常量的引用也应该是一个常量
#include <iostream>
using namespace std;
int main(){
const int a=10;//const修饰定义的常量
//int& ra=a;编译时出错
const int& ra = a;
system("pause");
int i =10;
const int &r_i = i;
return 0;
原因: 变量a是可读不可改写的,引用ra是可读可改写的,如果对ra改写,就会对a产生影响。所以会编译出错。
②一个变量也可以用一个常量引用来引用
#include <iostream>
using namespace std;
int main(){
int a=10;
int& ra=a;
const int& rb = a;//可以用一个常量rb来引用
return 0;
原因: 变量a是可读可改写的,引用rb是可读不可改写的。这是允许的。
还有一种特殊情况,当引用时发生隐式转化,用常引用接收可以。
如下:
#include <iostream>
using namespace std;
int main(){
double a=3.14;
//int& ra=a;//编译出错
const int& rb = a;//正确
return 0;
}
原因: 当发生隐式转化时,会产生一个临时变量,改临时变量先将原来值进行转化,再赋值给新值。重要的原因是,该临时变量具有常量性质(可读不可改写)。所以需要常引用接收。
用上面的例子就是,rb引用a时发生隐式转化,此时会创建一个临时变量tmp,先将a转化为int,再将tmp值赋给rb。
注意 是引用的时候发生隐式转化时,提前隐式转化,再引用不需要常引用接收。
#include <iostream>
using namespace std;
int main(){
double a=3.14;
int c = a;//提前转化,再引用
int& rc = c;//正确
return 0;
}
4、一个变量可以有多个引用。
#include <iostream>
using namespace std;
//这样都是正确的,一块空间的多个命名
int main(){
int a=10;
int& ra=a;
int& rb = a;
int& rc = ra;
return 0;
}
结论:引用时,读写权限可以缩小不可以放大。
5、引用场景
①引用变量
int a = 10;
int& b = a;//给a取别名为b
b = 100;
int& ra=a;
int& rb = a;
int& rc = ra;
cout << "a == " << a << endl;
一个变量可以有多个引用
②引用数组
int a[4] = {1,2,3,4};
int (&arr)[4] = a;
6、在函数中的引用
①做形参
#include <iostream>
using namespace std;
void swap(int& a,int& b){
int temp=a;
a=b;
b=temp;
}
int main()
{
int x=10;
int y=20;
swap(x,y);
return 0;
}
②做返回值
#include <iostream>
using namespace std;
int& count(){
static int n=0;
n++;
return n;
}
int main()
{
int& a=count();
cout<<a<<endl;
return 0;
}
1.这里为什么要用static修饰n,如果不用static修饰会怎么样?
上面返回值为int&引用,在main函数中用引用a来接收。我们知道引用并不开辟空间,所以a并没有开辟空间,而是n的引用,也就是n和a的地址会相同。如果不用static修饰n,则n变量在栈上开辟空间,如果调用一个其它的函数,a的值很有可能会发生改变,这样a的值得不到保障。但是static修饰的变量在静态区存储,值并不会发生改变。
2.返回值不是引用的,如果想用引用来接收,必须使用常引用。
#include<iostream>
using namespace std;
int count(){
int n = 0;
n++;
return n;
}
int main(){
//int& a = count();//编译错误
const int& a = count();//正确
return 0;
}
原因:和隐式转化差不多,当返回的时候,会生成一个临时变量,该临时变量的值等于返回值。并且,该返回值具有常量性质。
3.引用做返回值,也可以不使用引用来接收,这样值就得到了保障。
#include<iostream>
using namespace std;
int count(){
int n = 0;
n++;
return n;
}
int main()
{
int a = count();
return 0;
}
原因:此时main函数里的a开辟空间,与n无关了,或者说与返回时创建的临时变量无关了,只是将它的值赋给了a。
二、引用的效率
传值和传引用,返回值和返回引用的效率说明:
以值作为参数或者返回值,在传参数和返回期间,函数不会直接传递给实参或者将变量本身直接返回,而是传递实参或者返回变量的一份临时拷贝,由上可知要开辟临时空间。因此效率时很低下的,而使用引用作为参数和返回值时,并不需要开辟空间。
因此传值,返回值比传引用和返回引用的效率低。
三、指针与引用的不同点
1、引用在概念上是定义一个变量的别名,指针存储一个变量的地址。
2、引用在定义时必须初始化,指针没有要求。
3、引用在引用一个实体后,不能再引用其它实体,指针在任何时候都可以指向一个同类型的实体,改变指向。
4、没有NULL引用,指针可以指向NULL。
5、在sizeof中含义不同,引用的结果是引用类型的大小(int&类型是int)。但是指针始终是地址空间所占字节大小(在32位机器下占4字节空间,在64位机器下占8字节空间)
6、有多级指针,没有多级引用。
7、访问实体不同,指针需要显式解引用,引用编译器自己处理。
8、引用加1,为实体数值加1。指针加1,偏移一个单位。
9、引用比指针使用起来相对安全些。