引用变量是c++新增的 复合类型(&)。它是已定义变量的别名。例:创建引用变量
程序清单 :
int a=6;
int &b=a;
int c=&a; //注意这一行是将a的地址值赋给c
如此,我们就可以用 a或者引用变量b 来表示该变量。
此时,a和b指向相同的值和内存单元。
作用:主要用作函数的形参,通过引用我们可以将使用原始数据,而不是副本(类似指针)。引用的特性很适合我们处理大型结构。
我们说引用很类似指针,但是它们还是有区别的。
程序清单 1.1:
int a=6;
int &b=a;
int *p=&a;
此时:
a = b = *p =6 ;
&a = &b = p = a的地址;
所以说引用很类似于指针(都是指向同一内存单元),
但是,我们必须要注意的引用和指针的不同点:
1.引用必须 声明时将其初始化
2.引用初始化后不能改变指向
比如说这样时错误的:
程序清单 1.2:
int a;
int &b;
b=a;
注:并且初始化时不能直接赋值一个常量,就是说我们需要对一个变量引用。 更加直白的来说,是对地址的引用
程序清单1.3:
int &a=6;
这样即使初始化了也是错误的。
引用不能改变它的指向,意思就是更加接近const指针。
其实:
int &b=a;
实际就是:
int * const p=&a;
*p 和 b 都只能指向 a的值和内存单元。
为了更好的理解引用不能改变它的指向这一特性,我们看下面这个例子:
程序清单 1.4:
int a=1,b=0;
int &c=a;
c=b;
cout<<"a:"<<a<<" b:"<<b<<" c:"<<c<<endl;
cout<<"a:"<<&a<<" b:"<<&b<<" c:"<<&c<<endl;
。
。
。
运行结果:
我们从结果看出,虽然令 a的引用c等于了b,但是 a和c的地址还是一样的,并且修改c的同时也修改了a。(这个也是我们要求引用声明时就要初始化的理由)
函数的普通参数的特点:按值传递。这样的弊端是我们只能原始数据的副本,在很多场景下是有诸多问题的 — 比如说交换来两个变量的值。
但是我们将引用变量作为参数的时候,就可以使用原始数据。(指针也可以,但我们这里主要讲按引用传递。
程序清单 2.1:
void fun(int &a){
a=0;
}
int main(){
int x=1;
fun(x);
cout<<x;
return 0;
}
.
.
.
输出结果:
x =0
我们若是使用普通形参的话,是不能修改原始数据 x的值的。
当我我们不想修改数据的值,但是又想使用引用的话,我们可以使用常量引用。
void fun(const &a)
当我们试图在函数中修改 a 的值时候,会报错。
程序清单 2.2:
void fun( int &a){
...
...
}
int main(){
int x=6;
double y=66;
fun(x);
fun(y);
fun(6);
fun(x+6)
}
测试中的三个函数调用只有第一个 fun(x) 可以同通过。
我们前面讲过,只能对变量引用。
参数 6 显然不是变量,x+6同样也不是变量(表达式)。y 类型不匹配,因为形参只能接受 int类型的引用(这里不能强转,下面会讲)。
解决办法:
我们可以看出 传递引用更加严格,但是后期C++有了函数的引用参数传实参类型不匹配的解决办法:
当且仅当函数的形参为 const 引用时,如果实参与引用形参不匹配,c++会创建实参的临时无名变量。
程序清单 2.3:
void fun(const int &a){
...
...
}
int main(){
int x=6;
double y=66;
fun(x);
fun(y);
fun(6);
fun(x+6)
}
现在 实参 y ,6 ,x+6 都不会报错了。
例如 x+6 ,会为 x+6 创建 一个无名的临时变量,初始化为 x+6的值,那么 :a就是这个临时变量的引用。
注:
左值:能通过地址访问的值。例如:变量,数组元素,结构成员等都是左值。(常规变量和 const变量都是左值)
右值: 那当然不能地址访问的值。例如:字面常量,多项表达式。(用引号括起来的字符串不是右值,因为由其地址表示)
在这种情况下,编译器会创建一个临时变量,并让形参的引用变量指向它。值得注意的是,临时变量只会在函数调用期间存在。
C++11 新增的一种引用----右值引用。用 && 声明。
程序清单 2.4:
int mian(){
int a=6;
int & b=a+6;//这是错误的
int && c=a+6;//正确
将引用变量作为函数的返回值,称为引用返回。讨论引用返回和传统返回的不同之处。
返回机制和 函数按值传递参数很类似。
程序清单 3.1:
int add(int a,int b){
a=a+b;
return a;
}
int main(){
int x=2,y=3,z=0;
z=add(x,y);
return 0;
}
返回过程:
返回值a 的值被复制到一个临时位置,然后再将 临时位置的值复制 给目标变量c。(为什们要放在临时位置?因为变量a在函数结束时被销毁。)
注:其实函数的传统返回值是一个 右值。
程序清单 3.2:
int & add(int &a,int b){
a=a+b;
return a;
}
int main(){
int x=2,y=3,z=0;
z=add(x,y);
return 0;
}
返回过程:
直接把返回值a 复制到目标变量c。
总结:当我们在传输一些大型结构时,用引用返回效率更高,因为少了复制到临时位置这个步骤。
下面的程序可以运行吗?
程序清单3.3:
int & add(int a,int b){
a=a+b;
return a;
}
int main(){
int x=2,y=3,z=0;
z=add(x,y);
return 0;
}
.
.
.
.
不能!!
我们返回a的引用,但是a 在函数运行完毕后也不存在了。
当我们想返回引用时,怎们解决这个问题呢?
解决办法:
基类引用 可以指向 派生类对象,不用强转。
实际作用:
可以 将函数形参声明为 基类引用,则调用函数时的实参 可以是:
注:
当我们参数的数据对象是类对象时,函数形参应该使用 const 引用。