C++中的引用&
基本概念:
变量名的实质:
一段连续内存空间的别名。而一段连续的内存空间不是只能取一个别名。
引用的概念
在C++中,引用表示一个已定义变量的别名。
引用的语法
类型 &引用名 = 目标变量名;
void test0()
{
int a = 1;
//引用是变量的别名,提出引用的目的就是为了减少指针的使用
//操作ref与操作变量本身是一样的
int &ref1 = a;
//引用变量在定义的时候必须要初始化,引用变量不能独立存在
int &ref2; //error
}
使用引用的注意事项
- &在这里不再表示取地址符号,而是引用符号,相当于&有了第二种用法;
- 引用的类型必须和其绑定的变量的类型相同;
- 声明引用的同时,必须对引用进行初始化;否则编译时报错;
- 引用变量一经绑定,就不能再改变它的指向;
引用的本质
C++中,引用的本质是一种被限制的指针,即指针常量(*const
)。
类似于线性表和栈的关系,栈是被限制的线性表,底层实现相同,只不过逻辑上的用法不同而已。
由于引用是被限制的指针,所以引用是占据内存的,占据的大小就是一个指针的大小。
有很多的说法,都说引用不会占据存储空间,其只是一个变量的别名,但这种说法并不准确。引用变量会占据存储空间,存放的是一个地址,但是编译器阻止对它本身的任何访问,从一而终总是指向初始的目标单元。
在汇编里, 引用的本质就是“间接寻址”。在后面学习了类之后,我们也能看到相关的用法。
引用存在的意义
在没有引用之前,如果我们想通过形参改变实参的值,只有使用指针才能到达目的。但使用指针的过程
中,不好操作,很容易犯错。
而引用,可以作为其他变量的别名而存在。因此,在很多场合下,就可以使用引用代替指针,具有更好的可读性和实用性。
引用的用法
引用作为函数参数
经典例子:交换两变量的值。
//用指针作为参数
void swap(int *pa, int *pb)
{
int temp = *pa;
*pa = *pb;
*pb = temp;
}
//引用作为参数
void swap(int &x, int &y)
{
int temp = x;
x = y;
y = temp;
}
参数传递的方式:值传递、指针传递、引用传递。
系统会在内存中开辟空间用来存储形参变量,并将实参变量的值拷贝给形参变量,即形参变量只是实参变量的副本而已;如果函数传递的是类对象,系统还会调用类中的拷贝构造函数来构造形参对象,假如对象占据的存储空间比较大,那就很不划算了。使用指针作为函数的形参虽然达到的效果和使用引用一样,但当调用函数时仍需要为形参指针变量在内存中分配空间,而引用则不需要这样,故在C++中推荐使用引用而非指针作为函数的参数,这样会大大提高函数的时空效率。
当用引用作为函数的参数时,其效果和用指针作为函数参数的效果相当。当调用函数时,函数中的形参就会被当成实参变量或对象的一个别名来使用,也就是说此时函数中对形参的各种操作实际上是对实参本身进行操作,而非简单的将实参变量或对象的值拷贝给形参,减少了拷贝操作,提高了效率。
引用作为函数的返回值
语法:
类型 &函数名(形参列表)
{
//函数体
}
当以引用作为函数的返回值时,返回的变量其生命周期一定是要大于函数的生命周期的,即当函数执行完毕时,返回的变量还存在。
int gNumber;//全局变量
int func1() // 当函数返回时,会对temp进行复制
{
int temp = 100;
return temp;//此处会进行复制操作
}
int &func2()//当函数返回时,不会对temp进行复制,因为返回的是引用
{
int temp = 1000;
return temp;
}
//不要去返回局部变量的引用
当引用作为函数的返回值时,必须遵守以下规则:
- 不能返回局部变量的引用。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为
了"无所指"的引用,程序会进入未知状态。 - 不能在函数内部返回new分配的堆空间变量的引用。如果返回的引用只是作为一个临时变量出现,
而没有被赋予一个实际的变量,那么该引用所在的空间就无法释放,会造成内存泄漏。
//函数的返回类型是引用的前提:实体的生命周期比函数的生命周期要大
int arr[10] = {1, 4, 6, 8, 9, 10};
int &getIndex(int idx)
{
return arr[idx];//先不考虑越界等问题
}
void test3()
{
cout << "getIndex(0) = " << getIndex(0) << endl;
getIndex(0) = 100;
cout << "getIndex(0) = " << getIndex(0) << endl;
cout << "arr[0] = " << arr[0] << endl;
}
//不要去返回堆空间的引用,除非有自动内存回收的机制,不然就会造成内存泄漏
int &getHeapData()
{
int *pInt = new int(100);
return *pInt;
}
void test4()
{
int a = 3, b = 4;
/* int c = a + getHeapData() + b;//存在内存泄漏的问题 */
/* cout << "c = " << c << endl; */
//解决内存泄漏的一个方法
int &ref = getHeapData(); //引用
delete &ref; //取地址
}
引用的总结
- 在引用的使用中,单纯给某个变量取个别名是毫无意义的,引用的目的主要用于在函数参数传递
中,解决大块数据或对象的传递效率和空间不如意的问题。 - 用引用传递函数的参数,能保证参数传递中不产生副本,提高传递的效率,且通过const的使用,保证了引用传递的安全性。
- 引用与指针的区别是:
- 指针通过某个指针变量指向一个变量后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;
- 而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。