一、引用的定义
引⽤不是新定义⼀个变量,⽽是给已存在变量取了⼀个别名,编译器不会为引⽤变量开辟内存空间,它和它引⽤的变量共⽤同⼀块内存空间。例如你有身份证上的名字,但在家里可能父母会叫另外的乳名或者小名。
C++中为了避免引⼊太多的运算符,会复⽤C语⾔的⼀些符号,⽐如前⾯的<< 和 >>,这⾥引⽤也和取地址使⽤了同⼀个符号&。
类型& 引⽤别名 = 引⽤对象;
#include<iostream>
using namespace std;
int main()
{
//定义了变量a
int a = 0;
//给a取别名b,c
int& b = a;
int& c = a;
//也可以给别名b在取别名,d相当于还是a的别名
int& d = b;
// 这⾥取地址我们看到是⼀样的
cout << &a << endl;
cout << &b << endl;
cout << &c << endl;
cout << &d << endl;
return 0;
}
二、引用的特性
(1) 引⽤在定义时必须初始化
int main()
{
int a = 10;
// 编译报错:“ra”: 必须初始化引⽤
int& ra;
return 0;
}
(2)⼀个变量可以有多个引⽤
也就是一个变量可以起多个别名,在引用的定义已经演示过了。
(3)引⽤⼀旦引⽤⼀个实体,再不能引⽤其他实体
int main()
{
int a = 10;
int b = 20;
// 这⾥是⼀个赋值,而并⾮让c引⽤b,因为C++引⽤不能改变指向,
int& c = a;
c = b;
return 0;
}
三、引用的使用
3.1引用传参
- 引⽤在实践中主要是于引⽤传参和引⽤做返回值中减少拷⻉提⾼效率和改变引⽤对象时同时改变被引⽤对象。
因为引用并没有开辟空间
- 引⽤传参跟指针传参功能是类似的,引⽤传参相对更⽅便⼀些。
//这里的形参就是实参的别名
void swap(int& ra, int& rb)
{
int temp = ra;
ra = rb;
rb = temp;
}
int main()
{
int a = 10;
int b = 20;
cout << a << " " << b << endl;
//这里调用swap函数交换ab的值时,不需要在取地址
swap(a, b);
cout << a << " " << b << endl;
return 0;
}
3.2引用做返回值
这里是我们定义栈和栈的几个基本操作
#include<iostream>
#include<assert.h>
using namespace std;
typedef struct stack
{
int* data;
int capacity;
int top;
}ST;
//初始化
void STInit(ST& s, int n = 4)
{
s.data = (int*)malloc(n * sizeof(int));
s.top = 0;
s.capacity = n;
}
//入栈
void STPush(ST& s, int x)
{
//assert(s);
//扩容
if (s.capacity == s.top)
{
printf("扩容\n");
int newcapacity = s.capacity == 0 ? 4 : s.capacity * 2;
int* temp = (int*)realloc(s.data, newcapacity * sizeof(int));
s.data = temp;
s.capacity = newcapacity;
}
s.data[s.top] = x;
s.top++;
}
//获取栈顶元素
int& STTop(ST& s)
{
assert(s.top > 0);
return s.data[s.top - 1];
}
注意这里获取栈顶元素用到的就是引用做返回值
int& STTop(ST& s)
那这种好处就是可以对栈顶元素进行修改,例如经过下面的主函数调用后,栈里有1、2两个元素
int main()
{
ST s1;
STInit(s1);
STPush(s1, 1);
STPush(s1, 2);
cout << STTop(s1) << endl;
return 0;
}
此时若我们要修改栈顶元素,如果是传值的话,就无法进行,如下图所示:
原因是在传值返回的时候,返回的是栈的元素的临时拷贝,会存储在一块临时空间中,在对它进行修改的时候,并不会影响栈顶元素本身。
而在进行引用返回时,返回的就是栈的元素本身,对它的修改就能直接改变栈的元素。
引⽤和指针在实践中相辅相成,功能有重叠性,但是各有特点,互相不可替代。
四、const引⽤
可以引⽤⼀个const对象,但是必须⽤const引⽤。const引⽤也可以引⽤普通对象,因为对象的访问权限在引⽤过程中可以缩⼩,但是不能放⼤。
int main()
{
const int a = 10;
//对a访问权限的放大
//int& ra = a;
const int& ra = a;//只有这样才可以
return 0;
}
这里的引用就是对a访问权限的放大
但是可以缩小,这里的rb无法修改,但b可以修改
int main()
{
int b = 1;
const int& rb = b;//可以缩小
//rb++;
b++;
return 0;
}
五、引用和指针的关系
在实践中指针和引用相辅相成,功能有重叠性,但是各有⾃⼰的特点,互相不可替代。
- 语法概念上引⽤是⼀个变量的取别名不开空间,指针是存储⼀个变量地址,要开空间。
- 引⽤在定义时必须初始化,指针建议初始化,但是语法上不是必须的。
- 引⽤在初始化时引⽤⼀个对象后,就不能再引⽤其他对象;⽽指针可以在不断地改变指向对象。
- 引⽤可以直接访问指向对象,指针需要解引⽤才是访问指向对象。
- sizeof中含义不同,引⽤结果为引⽤类型的⼤⼩,但指针始终是地址空间所占字节个数(32位平台下占4个字节,64位下是8个字节)
- 指针很容易出现空指针和野指针的问题,引⽤很少出现,引⽤使⽤起来相对更安全⼀些。