本文是笔者在C++学习过程中,对引用语法的总结记录,如有不队的地方,欢迎大家指正!
一、 什么是引用
引用(reference),是C++对C语言的一项扩充,作用是给变量起别名,换句话说,就是将多个变量名指向同一个地址,从而使对其中的任意一个变量名的操作,都是对同一地址的操作。而在这种情况下,被声明为引用类型的变量名,就是实际变量名的一个别名。
二、引用的基本语法与注意事项
1、 声明语法
引用的声明语法:数据类型 &引用变量名 = 原变量名;
eg:
#include <iostream>
using namespace std;
int main() {
//语法:数据类型 &引用变量名 = 原变量名;
int a=10;
int &reference_a=a;
cout<<"a="<<a<<endl; //a=10
cout<<"reference_a="<<reference_a<<endl; //reference_a=10
a+=10;
cout<<"a="<<a<<endl; //a=20
cout<<"reference_a="<<reference_a<<endl; //reference_a=20
reference_a+=10;
cout<<"a="<<a<<endl; //a=30
cout<<"reference_a="<<reference_a<<endl; //reference_a=20
return 0;
}
2、 注意事项
对引用进行操作,实际上就是对被引用的变量进行操作。
声明引用时,必须对其进行初始化。
引用在初始化之后,不可以再进行修改。
-
引用变量在编译器的底层实现是指针常量,即一个指针指向不可以发生更改的指针,所以引用占用内存空间的大小,和指针其实是一样的。
但是,和指针不同的是,引用实际上不能算是一个变量,所以对引用变量求地址,与对实际变量求地址,结果是相同的。
#include <iostream>
using namespace std;
int main() {
int a=10;
int &reference_a=a;
//对引用变量求地址,与对实际变量求地址,结果是相同的
cout<<"对引用变量求地址"<<endl;
cout<<"a的地址:"<<&a<<endl;
cout<<"reference_a的地址:"<<&reference_a<<endl;
return 0;
}
运行结果:
-
不能建立引用的数组,即数组中的元素不能是引用。因为引用使劲上并不能算是一个变量,所以在声明引用的数组时,并不能分配内存,也就无法声明和定义引用数组。
但是,可以建立数组的引用,这是没有问题的。
#include <iostream>
using namespace std;
int main() {
//不能建立引用的数组,但是,可以建立数组的引用
int arr[]={0,1,2,3,4};
// int &re_arr=&arr;
// 编译错误
// error: invalid conversion from ‘int (*)[5]’ to ‘int’
//数组的引用
int (&re_arr)[5]=arr;
return 0;
}
三、引用作为函数参数
引用在C++中的一个重要作用,就是可以作为函数的参数使用。
在C语言之中,函数参数传递使用的方法是值传递,而在有大量数据作为参数传递时,往往采用的是地址传递,即使用指针,来避免在函数调用时有大量数据压入栈中。现在,C++中可以使用引用作为函数参数,以此来代替指针作为函数参数,这种方法可以使代码更易阅读和维护。
eg:
#include <iostream>
using namespace std;
//引用作为函数参数
void fun1(int &a,int &b);
int main() {
int a=10;
int b=20;
cout<<"a="<<a<<",b="<<b<<endl;
fun1(a,b);
cout<<"a="<<a<<",b="<<b<<endl;
return 0;
}
//引用作为函数参数
void fun1(int &a,int &b){
int temp=a;
a=b;
b=temp;
}
运行结果:
可以看出,在使用引用作为函数参数时,与使用指针作为函数参数的效果一致,也就是在调用方法的时候,可以用形参修饰实参。
四、引用作为函数返回值
C++支持将引用作为函数的返回值返回。
eg:
#include <iostream>
using namespace std;
//引用作为函数返回值
int& fun2(int &a);
int main() {
int b=10;
cout<<"fun2()前,b="<<b<<endl;
cout<<"fun2()后,b="<<fun2(b)<<endl;
return 0;
}
int& fun2(int &a){
a*=a;
return a;
}
运行结果:
注意事项
1. 不要返回局部变量的引用
因为局部变量是存放在栈区的,在函数返回后,局部变量就会被销毁,所以返回的引用就会像悬空指针一样,指向一个未知的空间。
eg:
#include <iostream>
using namespace std;
int& func2_local();
int main() {
int &ref_locol=func2_local();
// cout<<"ref_locol="<<ref_locol<<endl; //编译错误
cout<<"ref_locol的地址为:"<<&ref_locol<<endl; //输出地址为0
return 0;
}
// 不要返回局部变量的引用
int& func2_local(){
int a=10; //栈区,函数调用结束后释放
return a; //Reference to stack memory associated
// with local variable 'a' returned
}
运行结果:
2. 函数调用可以作为左值
eg:
#include <iostream>
using namespace std;
int& func2_left();
int main() {
int left=func2_left();
cout<<"left="<<left<<endl;
cout<<"func2_left()作为左值,修改为100"<<endl;
func2_left()=100;
cout<<"left="<<left<<endl;
return 0;
}
//函数调用可以作为左值
int& func2_left(){
static int a=10; //全局区,程序结束后由系统释放
return a;
}
运行结果:
3. 不要返回函数内部使用 new 分配的内存的引用
这种编程习惯是非常不好的,因为每个用 new 产生的指针都要调用 delete 释放,否则就会造成内存泄漏。而在被函数返回的引用作为一个临时变量时,我们很容易未对其进行释放,从而造成内存泄漏。
eg:
#include <iostream>
#include <string>
using namespace std;
string& func2_new();
int main() {
string &str=func2_new(); //如果调用后没有用 delete 释放,就会造成内存泄漏
cout<<"str="<<str<<endl;
delete &str; //释放
return 0;
}
//不要返回函数内部使用 new 分配的内存的引用
string& func2_new(){
string* str=new string("hello world");
return *str;
}
由代码可以看出,返回函数内部使用 new 分配的内存的引用时,极易引起内存泄漏,尤其是在形如以下代码的情况下:
string str="hello world"+func2_new();
五、常量引用/常引用
声明方式:const 数据类型 &引用名 = 目标变量名;
eg:
#include <iostream>
using namespace std;
string& func3();
int main() {
func3(a);
return 0;
}
//常量引用/常引用
void func3(const int &a){
cout<<"a="<<a<<endl;
}
常引用主要用于修饰形参,防止函数中误操作。另外,在引用作为函数参数时应该尽量定义为 const 。