引用
是C++中特有而C语言中没有的,我们知道,在C语言中,给函数传实参时,形式参数的改变不会影响原来实参的值,这里最经典的就是交换两个变量的值。
void swap (int a,int b)
{
int c = 0;
c = a;
a = b;
b = c;
}
int main()
{
int a1 = 2;
int b1 = 4;
swap(a1,b1);
cout<<a1<<endl<<b1<<endl;
return 0;
}
这里当我们运行后就会发现a1和b1的值并没有被改变,只是形式参数a和b的值在函数体内改变了,而当函数结束后,局部变量a和b跟着也会被销毁,对于a1和b1就像什么都没有发生过一样,在C语言中,要想实际改变a1和b1的值就需要在传参时传他们的地址,也就是传指针,这样能够解决问题,
#include <iostream>
using namespace std;
void Swap(int* a, int* b)
{
int c = 0;
c = *a;
a* = *b;
b* = c;
}
int main()
{
int a1 = 2;
int b1 = 4;
Swap(&a1, &b1);
cout << a1 <<endl<< b1 << endl;
return 0;
}
可是每次都传地址然后解引用略显繁琐,所以C++引入了“引用”的概念。我们先简单使用一下引用:
#include <iostream>
using namespace std;
void Swap(int& a, int& b)
{
int c = 0;
c = a;
a = b;
b = c;
}
int main()
{
int a1 = 2;
int b1 = 4;
Swap(a1, b1);
cout << a1 <<endl<< b1 << endl;
return 0;
}
引用的符号是&,这里与C语言中的&取地址符号相同,但是用法含义不同,要注意区分。引用简单来说就是取别名,为已存在的变量取一个别的名字,他们共同使用一块存储空间,所以在上面使用引用的例子中,传参数传的是引用,就相当于形参a是a1的一个别名,他们共同使用一块空间,对a的改变实际上也是对a1的改变,虽然在函数结束后a会被销毁,但是对a1的修改已经完成,b1相同。
引用出了做参数还可以做返回值,这也是他的重要作用之一。但是引用做返回值有很多需要注意的点,不然一不小心就会出错。如下程序:
#include <iostream>
using namespace std;
int& add(int a, int b)
{
int c = a+b;
return c;
}
int main()
{
int a1 = 2;
int b1 = 4;
int& sum = add(a1, b1);
cout << sum << endl;
return 0;
}
这里输出了6,乍一看没问题,可是看add函数返回值是c的引用,c是一个局部变量,在函数结束时函数栈帧销毁,c也就不存在了,就会出问题,所以在不同的编译器中这里sum的值可能为6也可能随机值,因而尽量不要返回局部变量的引用。一般用引用做返回值的是静态变量或全局变量,在C++中,返回引用很好的一个引用是返回*this引用,这里涉及到类与对象中的this指针,我们先不做详细介绍,后续到类与对象模块会详细介绍this指针。
引用在使用时必须初始化,必须在一开始就与某个变量绑定,并且这种绑定形成后,就无法成为别的变量的引用,换言之,引用的指向无法改变。
int a = 0;
int &b = a;//正确
int &c = b;
//正确,此时b就是a,所以c也可以成为a的别名,此时a,b,c具有相同的含义
int d= 0;
int &b = d;
//错误,b已经是a的别名
//此时相当于对b的多次定义,是错误的
常引用
常引用是C++中一种特殊的引用类型,即被const限定的引用,他表示对一个对象的只读访问。具体看以下代码:
const int a = 0;
int& b = a;//错误,a被const限定,代表a为常量不可被修改,所以无法赋值给引用变量b
int a = 0;
const int& b = a;//正确,虽然b为常引用,但是a可以赋值给b,代表b与a 绑定,且b只能与a绑定,无法绑定其他变量
int &c = a;//正确
int &d = b;//错误,b为常引用,这里相当于常量,无法赋值给引用变量d
int &e = 0;//错误
const &f = 0;//正确,可以给常引用初始化常量初值
根据上面的例子总结下来就是,普通引用变量初始化只能赋变量(左值),而常引用变量初始化可以为变量或常量,但是之后做右值。无法再赋值给新的引用变量。
下面介绍一种特殊的情况:
int i = 0;
double j = i;
double& k = i;//错误,i类型不是int
const double& k = i;
//正确,虽然i的类型不是double,但是在这个过程中会发生类型转换
//类型转化在转化过程中会生成临时变量,而对于内置类型来说这个临时变量具有常属性
//因此可以赋值给常引用
引用和指针的区别
在表面语法上,很明显的一个语法上的区别就是引用并没有开辟新的空间,与变量共用一块储存空间,而指针开辟了新的空间来存放指针变量。(实际上从底层来说,引用变量也开辟了新的空间,只不过现在不用管它,现在就认为引用不开辟新的空间就好)
在初始化方面,无论是常引用还是普通引用,引用在创建时是一定要初始化的,不然编译通不过,而指针不强制初始化(不过注意指针不初始化容易造成野指针的问题)。因此还有一个问题,指针是有空指针NULL(nullptr)的,而引用没有空引用,在语法上不允许。
从指向上来看,指针的指向是可以改变的,可以指向不同的空间,而引用的指向一旦确定,就不允许改变。
指针有二级指针,有指向指针的指针,引用没有,没有二级引用这样的概念,只能是多个引用指向同一个变量。
对引用进行自增自减实际上就是对引用所指向的变量的自增自减,而对指针变量的自增自减会造成指向空间地址的改变(前后移动)。
关于sizeof()运算符,sizeof(指针)是指针变量本身的大小,根据系统的不同(32/64位)为4/8字节,而sizeof(引用)是引用所指向的变量的实际的大小,因为引用就是变量的别名。
从访问角度来说,我们对于引用的使用是编译器来做的,所以我们能方便的使用,无需解引用,而我们对于指针指向内容的使用需要解引用操作符*来使用,需要我们自己来控制。
总而言之,引用是C++相比较于C语言的一个改进,我们使用上的简化都是编译器在帮我们操作,是编译器承担了所有。引用相对于指针操作来说更安全一些。
最后欢迎各位朋友们在评论区交流讨论。