引用–c++入门
这边文章主要为大家介绍c++新加入的特性–引用。下面让我们来一起看一下吧
1.引用概念
引用不是新定义一个变量,而是给已存在的变量取一个别名,编译器不会为引用变量开辟内存空间,它会和它引用的变量共用同一个内存空间。
具体的使用方式:类型& 引用变量名(对象名) = 引用实体
int a = 10; int& b = a;//int&即引用a,为a去别名b
注意:引用和取地址是两种东西。
2.引用的特性
- 引用在定义时必须要初始化
- 一个变量可以有多个引用
- 引用了一个实体后,该引用不能再引用其他实体
3.引用的使用场景
- 做参数
//例如交换两个变量的值时,不需要再传两个参数的地址, //原本形参是实参的临时拷贝,使用引用后就变成形参是实参的别名,即可以直接改变实参 void swap(int& a, int& b) { int tmp = a; a = b; b = tmp; }
做返回值
返回的值必须是静态的,不能是函数栈帧内的临时变量。
int& Count() { static int a = 0; a++; return a; }
4.使用引用可能会出现的问题
常引用
引用过程中,权限只能平移或缩小
void TestConstRef() { const int a = 10; //int& ra = a; //因为a为常量,是常量的值是不能改变的 //ra++; //此时如果改变ra的值,程序就会出问题 const int& rra = a; //正确用法 //int& b = 10; //因为10是常量,这里的问题是权限的放大 const int& b = 10; //正确用法 double d = 3.14; //int& i = d; //类型不同不能引用 const int& ii = d; //正确用法,因为不同类型的转换之间会生成临时变量,临时变量具有常性 }
返回值引用
返回值引用的问题会涉及到语言设计上的问题,这个问题我会在下面讲述。
//返回值引用的错误使用 int& Add(int x, int y) { //在函数栈帧内定义的变量基本为临时变量 //在函数结束后栈帧会销毁,导致返回了类似野指针的变量 //因为在接收返回值之前,返回值会先存储在栈帧的临时变量上 //如果接受了这个临时变量就会出现访问野指针的问题 int sum = x + y; return sum; } //正确用法 int& Add(int x, int y) { static int sum = x + y; //使用静态区的变量 return sum; }
注意:使用引用做返回值时需要谨慎,若返回值出了函数的作用域,对象不在了就不能引用返回,反之还在的话才可以使用引用返回。
5.传值和传引用的区别
以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递参数给实参或者将变量本身直接返回,而是在传值的过程中生成一份临时拷贝,因此在用值作为参数或返回值类型时,其效率相较传引用差,尤其在参数或者返回值较大的情况下,效率就会更低。
使用下面的代码测试可以清晰反映出两者之间的效率差距:
struct A { int a[10000] = { 0 }; }; void Func1(A a){} void Func2(A& a) {} void TestRefAndValue() { A a; //传值 size_t begin1 = clock(); for (size_t i = 0; i < 10000; i++) { Func1(a); } size_t end1 = clock(); //传引用 size_t begin2 = clock(); for (size_t i = 0; i < 10000; i++) { Func2(a); } size_t end2 = clock(); //两种函数运行时间 cout << "Func1_Time:" << end1 - begin1 << endl; cout << "Func2_Time:" << end2 - begin2 << endl; }
其运行结果如下:
同理,传返回值也是同样的道理。
总结来说就是:
- 引用做参数可以减少输出型参数或是减少拷贝从而提高效率
- 引用做返回值可以减少拷贝或是读取和修改返回值
6.引用和指针的区别
上面在介绍使用引用会出现的问题时提到了会存在访问野指针的问题,但引用为什么会出现类似问题呢?这就需要我们了解引用和指针的区别了。
在语法层面上,引用就是对变量取别名,与引用的实体共用同一块空间,没有独立的空间;而指针则需要开空间存放该实体的地址。
在底层实现上,引用是按照指针的方式来实现的,所以在实际使用上,若没有稍加注意引用的对象,就可能会出现类似指针访问野指针的问题。但是,在实际开发的过程中,我们一般会认为引用是不开空间的。
例如下面代码:
int a = 10; int& ra = a; int b = 20; int* pb = &b;
下面是上述代码的汇编代码:
可见引用和指针在底层实现上几乎是一样的。
引用和指针的不同点:
- 引用在概念上是定义一个变量的别名,而指针在概念上是存储一个变量的地址
- 引用在定义是必须初始化,指针没有要求
- 引用在初始化时必须引用一个实体,且之后不能改变其引用的实体;而指针可以在任何时候指向任何一个同类型的实体
- 没有NULL引用,但指针会有NULL指针
- 在sizeof中含义不同:引用结果为引用类型的大小,而指针则是**地址空间所占字节个数(**32位与64位平台下字节个数也不同)
- 引用自加即引用的实体增加,指针自加即指针向后偏移一个类型的大小
- 引用没有多级引用,但指针有多级指针
- 两者访问实体方式不同,编译器会自己处理引用的访问,而指针需要解引用符进行访问
- 引用比指针使用起来相对安全