C++初级主题--(4)引用

本文深入探讨C++中的引用概念,包括其与传值的区别、如何使用引用进行参数传递以提高程序效率,以及引用的多种分类形式。此外还讨论了引用在函数中的应用,并给出了一套实用的参数传递准则。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.引用的概念

C++函数中参数的传递方式是传值。在函数域中为参数重新分配内存,而把实参的数值传递到新分配的内存中。它的优点是有效避免函数的副作用(即改变实参的值)。
如果要求改变实参的值,怎么办呢?如果实参是一个复杂的对象,重新分配内存会引起程序执行效率大大下降,怎么办呢?在C++中有一种新的导出型数据类型—引用(reference)可以解决上面的难题。引用又称别名(alias)。

二.传值和传引用的区别

1.用交换程序举例说明
(1)直接通过传值的方式无法更改实参的值

#include<iostream>
using namespace std;

void swap1(int x, int y)
{
    int tmp = y;
    y = x;
    x = tmp;
}
void main()
{
    int a = 10;
    int b = 20;
    swap1(a,b);
}

a,b的值仍未交换,只交换了x,y的值。
这里写图片描述
(2)C语言中通过指针的方式可以交换

#include<iostream>
using namespace std;

void swap1(int *x, int *y)
{
    int tmp = *y;
    *y = *x;
    *x = tmp;
}
void main()
{
    int a = 10;
    int b = 20;
    swap1(&a,&b);
}

可以看到x,y与a,b的地址不同,额外为形参开辟了空间
这里写图片描述
(3)C++中提供引用的方式

#include<iostream>
using namespace std;

void swap1(int &x, int &y)
{
    int tmp = y;
    y = x;
    x = tmp;
}
void main()
{
    int a = 10;
    int b = 20;
    swap1(a,b);
}

可以看到x,y的地址与a,b相同,是a,b的别名,没有重新分配空间。
这里写图片描述

2.总结
(1)传引用可以直接更改实参的值
(2)传引用不用额外分配空间保存实参的数值。
(3)不能定义空引用,即引用的对象必须存在。
3.引用的本质仍是指针
关于这一点,请参考博文 c++ 引用 底层实现机制

  1. 引用是在编译的过程中被处理的,实际上就是在编译层面对程序员进行的一个比较友好的语法,而在实现上是由编译器完成了地址的传递,实质上还是指针。
  2. 不能简单的理解为一个别名,我们可以这样用,但是要知道底层就是一个指针变量,是要占用内存空间的,和define是不一样的。

三.引用的分类

1.变量的引用

int a = 10;
int b = &a;

b是a的引用

2.指针的引用

int a = 10;
int *p = &a;
int *&q = p;

q是p的引用

3.数组的引用

int ar[10] = {0};
int (&br)[10] = ar;

br是ar的引用

4.常量的引用
(1)常量必须用常量引用

const int x = 100;
const int &y = x;

(2)变量可以用常量引用

int x = 100;
const int &y = x;

(3)不同类型间进行常引用

double x = 13.14;
const int &y = x;

这里写图片描述可以看到y与x的地址不同,此时,不同类型之间进行赋值运算,一定会产生临时对象(假设为tmp),最终y是tmp的引用。同时,应注意:对于所有的临时对象,必须同样假设它们是不可存取的,即具有常量的性质。当改变这种数据时,编译器会指出错误,这是非常有用的提示,因为这个改变会导致信息丢失。

5.函数中的引用,即函数返回引用或函数参数中包含引用

(1)最经常看见引用的地方是在函数参数和返回值中。当引用被用作函数参数时,在函数内任何对引用的更改将对函数外的参数产生改变。
(2)当然可以传递一个指针来做相同的事情,但引用具有更清晰的语法。(可以把引用看作一个使语法更加便利的工具。)
(3)如果函数返回一个引用,必须像从函数返回一个指针一样对待。当函数返回时,无论引用关联的是什么都应该存在,否则,将不知道指向哪一个内存

int* fun1(int *x)    //(2)
{
    (*x)++;
    return x;    //正确,x指向确定的内存
} 
int& fun2(int &x)    //(1)
{
    x++;
    return x;    //正确,x指向确定的内存
}
int& fun3()
{
    int q;
    //return q;    //错误,局部变量,最终指向不明确的内存
    static int p;
    return p;    //正确,尽管fun3运行结束,但是p被static修饰,为全局变量,生存作用域仍存在,指向明确的内存
}
int main()
{
    int a = 10;
    fun1(&a);   //ugly(but explicit)
    fun2(a);    //clean(but hidden)  
}

四.参数传递准则

当给函数传递参数时,人们习惯上通过常量引用来传递。虽然看起来似乎仅是出于效率考虑(通常在设计与装配程序时并不考虑效率),但是这样会带来很多危险。拷贝构造函数需要通过传值方式传递对象,但并不总是可行的
这种简单习惯可以大大提高效率:传值方式会调用构造函数和析构函数,但是如果不想改变参数,则可以通过常量引用传递,它仅需要将地址压栈。

事实上,只有一种情况不适合用传地址方式。就是当传值是唯一安全的途径,否则将会破坏对象时。所以需要依据上下文

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值