C++引用总结
引用:
- C++新增了一种复合类型——引用变量。引用是已定义的变量的别名,新名字与旧名字有相同的值和内存地址。
创建引用变量
- 创建引用格式:type &new_varname = old_var;(调用引用必须要初始化)
int old;
int &newname;
newname = old;//这种方式是不行的
- 引用不光可以引用标识符(变量等),也可以引用立即数(右值、字面值、常量),但必须要加const(例如:int const &var = 40;),即常量引用。
- 引用是一次性的,一旦与某个变量关联起来,就将一直效忠于它,无法再次更改。
看个例子
int rats = 101;
int* p = &rats;
int &rodent = *p;
int bunnies = 50;
p = &bunnies;
cout << rodent << endl;
程序运行结果为101,说明rodent引用的是rats。(*p为rats所在的内存地址的内容,引用*p即引用rats),若要引用指针则要这样定义:int* &rodent = p;
- 不能空引用,引用使用不当会产生野引用(悬空引用)。
- 引用可以当做函数的参数,它引用的对象就是函数的实参(函数将可以使用原始数据,而不是数据值的拷贝),这种传递参数的方法被称为引用传递
优点:
a.函数之间可以共享参数
b.提高参数的传递效率(比指针还要高)
c.可以获取并修改参数
为了更好的比较,下面通过一个常见的问题——交换两个变量的值,对使用引用、指针和值传递做一下比较。跳转代码
引用的属性和特别之处
#include <iostream>
//计算参数的立方
double cube(double a)//值传递
{
a* = a*a;
return a;
}
double refcube(double &ra)//引用传递
{
ra *= ra* ra;
return ra;
}
int main()
{
using namespace std;
double x = 3.0;
cout << cube(x) << " = cube of " << x << endl;
cout << refcube(x) << " = cube of " << x << endl;
return 0;
}
我们知道,refcube()是一个引用传递的函数,那么计算立方后,x经过引用传递应该也会变成27,可是结果却是:
其实这不是因为引用传递失败了,造成这样的原因是由于cout的特性:参数从右到左入栈,即x还在x=3时就先入栈了。
回到引用,实际上refcube函数使得x的值变成了27,如果程序员的意图是让函数使用传递给它的信息,而不对这些信息进行修改,同时又想使用引用,则应该使用常量引用,即函数应该定义为:
double refcube(const double &ra)
{
...
//但此时不能再修改ra的值,ra是一个只读形参
}
所以,如果要编写类似上述类型的代码(即使用基本数据类型),应采用值传递的方式。当数据比较大(如结构和类)时,引用参数将很有用。
- 按值传递的函数,可以使用多种类型的实参,以上述的cube为例:
double z;
z = cube(x+2.1);//1
z = cube(3.14);//2
int y = 10;
z = cube(y);//3
以上的调用都是合法的
- 但对于refcube(double &ra)来说,如果ra是一个变量的别名,则实参也应该是变量。下面的代码就不合理——表达式x+3.0并不是变量
double num = refcube(x+3.0);
将引用用于结构和类
实际上,引入引用主要是为了用于这些类型,使用结构(类)引用参数的方式与使用基本变量引用相同。假设有如下结构定义:
struct student
{
char name[20];
short age;
int roomnum;
};
则可以这样编写函数原型,在函数中将指向该结构的引用作为参数
void change_room(student & stu);
实例一下:
void change_room(student & stu)
{
stu.roomnum = 0;
}
student stu1 = {"小明",16,3209};//初始化一个学生结构变量
change_room(stu1);//调用函数
由于change_room(stu1)的形参stu为引用,因此stu指向了stu1,函数体设置了成员stu1.roomnum,就这里而言按值传递不可行,因为这将导致设置的是stu1的临时拷贝的成员roomnum。
如果不希望函数修改传入的结构,可使用const关键字
void show(const student & stu)
{
std::cout << stu.name << stu.age << stu.roomnum << std::endl;
}
由于show()显示的是结构的内容而不修改它,就这个函数而言,也可按值传递,但与复制原始结构的拷贝相比,使用引用可节省时间和内存。
将引用用于返回值
- 为何要返回引用?
传统返回机制与按值传递函数参数类似:计算关键字return后面的表达式,并将结果返回给调用函数。从概念上来说,这个值被复制到一个临时位置,而调用程序将使用这个值。
如果函数返回一个结构,而不是指向结构的引用,将会先把整个结构复制到一个临时位置,再将这个结构拷贝复制给调用的程序。但在返回值为引用时,将直接把源结构复制到目标结构。 - 返回引用时需要注意的问题:
返回引用时最重要的一点:避免返回函数终止时不再存在的内存单元引用,即返回局部变量(或指向局部变量的指针)的引用。
解决方法有:1.返回一个作为参数传递给函数的引用。
2.用new来分配新的存储空间。
交换函数比较
#include <iostream>
class swaptest
{
public:
int x;
int y;
swaptest(int a,int b):x(a),y(b) { }//构造函数初始化类成员x,y
void swapv(int a,int b)
{
int temp = a;
a = b;
b = temp;
}
void swapp(int* a,int* b)
{
int temp = *a;
*a = *b;
*b = temp;
}
void swapr(int &a,int &b)
{
int temp = a;
a = b;
b = temp;
}
~swaptest(){ }
};
int main(int argc, char const *argv[])
{
using std::cout;
using std::endl;
swaptest test(3,10);
cout << test.x << " "<< test.y << endl;
test.swapv(test.x,test.y);
cout << test.x << " " << test.y << endl;
test.swapp(&test.x,&test.y);
cout << test.x << " " << test.y << endl;
test.swapr(test.x,test.y);
cout << test.x << " " << test.y << endl;
return 0;
}
执行结果
各函数执行后结果 | 变量x | 变量y |
---|---|---|
初始 | 3 | 10 |
swapv()值传递 | 3 | 10 |
swapp()指针 | 10 | 3 |
swapr()引用 | 3 | 10 |
可以看到,只有值传递的函数没有交换变量的值。引用和指针都成功交换了变量的内容。
- 先看值传递。swapv函数的操作是将x,y进行对调。需要注意的是,对形参的操作不会影响到a,b。当test.x,test.y把值赋给a,b之后,对a,b不论再做什么操作,都不会影响到test.x,test.y本身。
swapp(int *a, int *b)//函数声明
swapp(&test.x, &test.y)//函数调用
- 再看地址传递,如上。注意,这时的函数的声明和调用的写法与值传递不同。
但是与值传递的分析一样,我们可以设想,在swapp函数里,a=&test.x; b=&test.y;表示test.x的地址代入到了a,test.y的地址代入到了b。这样一来,对a, b的操作就是test.x,test.y本身的操作。所以test.x,test.y的值被对调了
swapr(int &a, int &b)// 函数声明
swapr(test.x, test.y)//函数调用
- 引用传递的函数声明和调用,如上。前面说过,在定义引用变量时必须对其进行初始化。实际上,swapr()函数调用时使用实参test.x,test.y初始化了形参a,b,因此函数的引用参数被初始化为函数调用传递的实参。所以,对引用参数a,b的操作实际上就是对test.x,test.y的直接操作。
返回
上一篇: c++学习 打卡第三天《引用》 补发