1.引用的定义
引用:给变量取一个别名,编译器不会为引用变量开辟内存空间,它与引用的变量共用同一块内存空间。
语法:
类型 & 引用变量名 = 引用实体
注意:引用必须初始化
引用一旦初始化 就不能再次修改别名
int num = 10;
int &a = num; //num的别名为a
int data = 20;
a = data; //不是data别名为a 而是将data值赋值a(num)
2.引用作用于普通变量
int num = 10;
int &a = num;//a就是num的别名 a==num
cout<<"num = "<<num<<endl;//10
//对a赋值 == 对num赋值
a=100;
cout<<"num = "<<num<<endl;//100
//a是num的别名 所以num和a具有相同的地址空间
cout<<"a 的地址:"<<&a<<endl;
cout<<"num 的地址:"<<&num<<endl;
3.引用作用于数组
方式1:
void test02()
{
int arr[5] = {10,20,30,40,50};
//需求:给arr起个别名
int (&my_arr)[5] = arr;//my_arr就是数组arr的别名
int i=0;
for(i=0;i<5;i++)
{
cout<<my_arr[i]<<" ";
}
cout<<endl;
}
方式2:配合typedef
void test03()
{
int arr[5] = {10,20,30,40,50};
//1、用typedef 给数组类型 取个别名
//TYPE_ARR就是一个数组类型(有5个元素 每个元素位int)
typedef int TYPE_ARR[5];
//myArr就是数组arr的别名
TYPE_ARR &myArr=arr;
int i=0;
for(i=0;i<5;i++)
{
cout<<myArr[i]<<" ";
}
cout<<endl;
}
上面的TYPE_ARR就表示是一个有5个元素 每个元素为int型的 数组类型
TYPE_ARR &myArr=arr; 就表示给arr取了一个myArr的别名
4.引用作用于函数的参数
void my_swap2(int *a, int *b)//a=&data1,b =data2;
{
int tmp = *a;
*a = *b;
*b = tmp;
}
int main()
{
int data1 = 10;
int data2 = 20;
my_swap2(&data1, &data2);
cout<<"data1 = "<<data1<<", data2 = "<<data2<<endl; //20 10
}
在C++中可以用以上这种指针的方式交换两个数的值。
void my_swap3(int &a, int &b) //a=data1,b=data2
{
int tmp = a;
a = b;
b= tmp;
}
int main()
{
int data1 = 10;
int data2 = 20;
my_swap3(data1,data2);//交换成功(推荐)
cout<<"data1 = "<<data1<<", data2 = "<<data2<<endl;
}
如上用引用的方式也能交换两个数,并且比用指针的方式更简单。
5.引用作用于函数的返回值
//引用作为函数的返回值类型
int& my_data(void)
{
int data = 10;
static int num = 200;
return num; //函数返回啥变量 引用就是该变量的别名
//函数的返回值是引用时 不要返回局部变量
}
int main()
{
//ret是别名 ret是num的别名
int &ret = my_data();//ret1是num的别名
cout<<"ret = "<<ret<<endl;
}
这里要注意的是,将引用作为函数的返回值时,函数返回的变量不能是一个局部变量。
例如在上面代码中return data; 这样就不行,因为局部变量在函数return后就被销毁了,系统为它所开辟的空间也没了,此时打印ret会造成非法访问内存。
6.指针的引用
void my_str1(char **p_str)//p_str = &str
{
//*p_str == *&str == str
*p_str = (char *)calloc(1,32);
strcpy(*p_str, "hello world");
return;
}
void my_str2(char* &my_str)//char* &my_str = str;my_str等价str
{
my_str = (char *)calloc(1,32);
strcpy(my_str, "hello world");
return;
}
int main()
{
char *str = NULL;
//需求:封装一个函数 从堆区 给str申请一个空间 并赋值为"hello world"
//my_str1(&str);
my_str2(str);
cout<<"str = "<<str<<endl;
free(str);
}
str2函数跟str1函数相比,语法更加清晰,函数参数变成指针的引用,用不着取地址。
7.常引用
既降低开销,又防止函数内部修改
typedef struct
{
int num;
char name[32];
}STU;
void myPrintSTU1(STU tmp)//普通结构体变量作为形参 开销太大
{
cout<<sizeof(tmp)<<endl;
cout<<"学号:"<<tmp.num<<", 姓名:"<<tmp.name<<endl;
}
void myPrintSTU2(const STU &tmp)//STU &tmp=lucy;tmp是lucy的别名 tmp没有开辟独立空间
{
//tmp.num = 2000;//err 因为tmp为常引用
cout<<"学号:"<<tmp.num<<", 姓名:"<<tmp.name<<endl;
}
int main()
{
STU lucy = {100,"lucy"};
//需求:定义一个函数 遍历lucy成员(读操作)
myPrintSTU2(lucy)
return 0;
}
以上代码中我们想遍历lucy成员,可以用myPrintSTU1的方式,但是开销太大,一个形参tmp就有36字节,于是我们可以用引用的方式来做形参(myPrintSTU2的方式),因为tmp没有开辟独立的空间,这样开销就会小很多。然而我们为了防止lucy里的数据被修改,就可以在引用前加一个const修饰,于是tmp.num的值就不能被修改,这就是常引用。
8.引用与指针的区别
相同点:
都是地址的概念;
指针指向一块内存,它的内容是所指内存的地址;而引用则是某块内存的别名。
不同点:
程序为指针变量分配内存区域;而不为引用分配内存区域;
指针解引用时要在前加“ * ”,引用可以直接使用;
引用只能在定义时被初始化一次,之后不可变;指针可变;
“sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本 身的大小;
不存在引用的引用,但可以有指针的指针;
引用是类型安全的,而指针不是(引用比指针多了类型检查;