C++ 引用

变量名实质上是一段连续内存空间的别名,是一个标号(门牌号)
程序中通过变量来申请并命名内存空间
通过变量的名字可以使用存储空间

1、引用的基本特性

在c/c++中指针的作用基本都是一样的,但是c++增加了另外一种给函数传递地址的途径,这就是按引用传递(pass-by-reference)

引用就是取别名,声明一个标识符为引用,就表示该标识符是另一个对象的外号

注意:C++中的引用是一种取别名的机制,而C语言中的指针是一种数据类型(代表内存编号的无符号整数)

引用必须初始化,不存在空引用,但有悬空引用(函数调用结束,释放,名还留着)
可以引用无名对象和临时对象,但必须使用常引用
引用不能更换目标,引用一旦完成了定义和初始化就和普通变量名一样了,它就代表了目标,一经引用终身不能再引用其它目标
引用目标如果具备const属性,那么引用也必须带const属性

一个变量可以有n个别名,它们的地址都是相同的

int a = 10;
//给变量a取一个别名b
int& b = a;
//操作b就相当于操作a本身
b = 100;
可以建立对数组的引用。
//1. 建立数组引用方法一
typedef int ArrRef[10];
int arr[10];
ArrRef& aRef = arr;
for (int i = 0; i < 10;i ++)
{
    aRef[i] = i+1;
}
    
//2. 建立数组引用方法二
int(&f)[10] = arr;
for (int i = 0; i < 10; i++)
{
    f[i] = i+10;
}

2、引用型参数

引用当作函数的参数能达到指针一样的效果,但不具备指针的危险,还比指针方便
引用可以简单实现函数间共享变量的目的,而且是否使用引用由被调函数说了算
引用当作函数的参数能提高传递参数效率,指针至少需要4字节内存,而引用只需要增加一条标识符与内存之间的绑定(映射)
当引用被用作函数参数的时,在函数内对任何引用的修改,将对函数外的被引用的变量产生改变。

//值传递
void ValueSwap(int m,int n)
{
    int temp = m;
    m = n;
    n = temp;
}
//地址传递
void PointerSwap(int* m,int* n)
{
    int temp = *m;
    *m = *n;
    *n = temp;
}
//引用传递
void ReferenceSwap(int& m,int& n)
{
    int temp = m;
    m = n;
    n = temp;
}
void test()
{
    int a = 10;
    int b = 20;
    //值传递
    ValueSwap(a, b);
    cout << "a:" << a << " b:" << b << endl;
    //地址传递
    PointerSwap(&a, &b);
    cout << "a:" << a << " b:" << b << endl;
    //引用传递
    ReferenceSwap(a, b);
    cout << "a:" << a << " b:" << b << endl;
}

通过引用参数产生的效果同按地址传递是一样的,引用的语法更清楚简单
C++主张用引用传递取代地址传递的方式,因为引用语法容易且不易出错。

3、引用型返回值

不要返回局部变量的引用,会造成悬空引用

//返回局部变量引用
int& TestFun01()
{
    int a = 10; //局部变量
    return a;
}
int& func(int& num) //这样是可以通过的
{
    return num;
}

可以返回静态变量引用 

//返回静态变量引用
int& TestFun02()
{    
    static int a = 20; 
    cout << "static int a : " << a << endl;
    return a;
}

 如果函数做左值,那么必须返回引用

int TestFunc02()   //改成 int& TestFunc02() 就可以了
{    
    static int a = 20; 
    cout << "static int a : " << a << endl;
    return a;
}

TestFunc02() = 100;  //错误: 赋值运算的左操作数必须是左值   

如果返回值是一个临时对象(右值),如果非要使用引用接收的话,必须使用常引用

int func(int num)
{
    return 100;
}

int main()
{
    int num=10;
    int& n=func(num);  // 改成 const int& n = func(num);就好了
    cout<<n<<endl;  
}

4、指针引用

struct Teacher{
    int mAge;
};
//指针间接修改teacher的年龄
void Pointer(Teacher** teacher){
    *teacher = (Teacher*)malloc(sizeof(Teacher));
    (*teacher)->mAge = 200;  
}
//引用修改teacher年龄
void Reference(Teacher*& teacher){
    teacher->mAge = 300;
}
void test(){
    //创建Teacher
    Teacher* teacher = NULL;
    //指针间接赋值
    Pointer(&teacher);
    cout << "Pointer:" << teacher->mAge << endl;
    //引用赋值,将teacher本身传到Reference函数中,函数参数变成指针的引用,用不着取指针的地址
    Reference(teacher);
    cout << "Reference:" << teacher->mAge << endl;
    free(teacher);
}

5、常量引用

void test01(){
    int a = 100;
    const int& aRef = a; //此时aRef就是a
    //aRef = 200; 不能通过aRef的值
    a = 100; //OK
    cout << "a:" << a << endl;
    cout << "aRef:" << aRef << endl;
}
void test02(){
    //不能把一个字面量赋给引用
    //int& ref = 100;
    //但是可以把一个字面量赋给常引用
    const int& ref = 100; //int temp = 200; const int& ret = temp;
}

常量引用主要用在函数的形参,尤其是类的拷贝/复制构造函数。

将函数的形参定义为常量引用的好处:

  1.引用不产生新的变量,减少形参与实参传递时的开销。

  2.由于引用可能导致实参随形参改变而改变,将其定义为常量引用可以消除这种副作用。

如果希望实参随着形参的改变而改变,那么使用一般的引用,如果不希望实参随着形参改变,那么使用常引用。

const int& param防止函数中意外修改数据

四、引用的本质

引用的本质在c++内部实现是一个指针常量:

Type& ref = val; // Type* const ref = &val;

c++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小与指针相同,只是这个过程是编译器内部实现,用户不可见。

//引用,转换为 int* const ref = &a;
void testFunc(int& ref){
    ref = 100; // ref是引用,转换为*ref = 100
}
int main(){
    int a = 10;
    int& aRef = a; //自动转换为 int* const aRef = &a;这也能说明引用为什么必须初始化
    aRef = 20; //内部发现aRef是引用,自动转换为: *aRef = 20;
    cout << "a:" << a << endl;
    cout << "aRef:" << aRef << endl;
    testFunc(a);
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值