C++中的引用

C++中的引用

标签: c++


1. 变量名回顾

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

2. c++的引用

    引用是C++的概念,属于C++编译器对C的扩展。
int main()
{
    int a = 0;
    int &b = a; //int * const b = &a 
     b = 11;  //*b = 11;

    return 0;
}
/**
结论:请不要用C的语法考虑 b=11
*/
    引用概念:
    + 在C++中新增加了引用的概念
    + 引用可以看作一个已定义变量的别名
    + 引用的语法:Type& name = var;
    + 引用做函数参数呢?(引用作为函数参数声明时不进行初始化)
#include <iostream>
using namespace std;
int main()
{
    int a = 10;
    int &b = a;

    //b是a的别名,请问c++编译器后面做了什么工作?
    b = 11;

    cout<<"b--->"<< a << endl;
    printf("a: %d\n", a);
    printf("b: %d\n", b);
    printf("&a: %d\n", &a);
    printf("&b: %d\n", &b);  //对同一块内存空间可以取多个别名
    system("pause");
    return 0;
}
/**
b--->11
a: 11
b: 11
&a: 3798852
&b: 3798852
*/
    结论:1. 普通引用在声明时必须用其它的变量进行初始化
    为什么必须初始化?   --> 结论:很像一个只读的常量

3. 引用的意义

    1. 引用作为其它变量的别名而存在,因此在一些场合可以代替指针
    2. 引用相对于指针来说具有更好的可读性和实用性
    比较下面实现同样功能的代码:
#include <iostream>
using namespace std;

int swap(int &a, int &b)
{
    int c = 0;
    c = a;
    a = b;
    b = c;
}

int swap(int *a, int *b)
{
    int c = 0;
    c = *a;
    *a = *b;
    *b = c;
}

//引用和左值进行绑定时,将设计出高质量的程序
//引用像一个常量,能起到指针的作用
//引用和指针有关系吗?
//引用有内存空间
void main()
{
    int a = 10;
    int a1 = 20;
    int &b = a; // type & 引用的名字 = 被引用的变量
    char buf[100];
    //在使用的时候,引用相当于变量的别名
    b = 11; // *b = 11; 
    swap(a, b);
    swap(&a, &b);
}

4. 关于引用

    普通引用有自己的内存空间吗?回答是肯定的:
#include <iostream>
using namespace std;

struct Teacer {
    int &a;
    int &b;
};
int main()
{
    printf("sizeof(Teacher) %d\n", sizeof(Teacer));
    system("pause");
    return 0;
}
/**
结果是:
sizeof(Teacher) 8
*/
    我们可以得出结论:引用是一个有地址,引用是常量。
    引用的本质:
    + 引用在C++中的内部实现是一个常指针
        Type& name <--> Type* const name
    + C++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小与指针相同。
    + 从使用的角度,引用会让人误会其只是一个别名,没有自己的存储空间。这是C++为了实用性而做出的细节隐藏
    请仔细对比间接赋值成立的三个条件:
    1. 定义两个变量 (一个实参一个形参)
    2. 建立关联,实参取地址传给形参: p = &a;
    3. 利用*p形参去间接的修改实参的值

    结论:引用在实现上,只不过是把间接赋值成立的三个条件的后两步合二为一。当实参传给形参引用的时候,只不过是c++编译器帮我们程序员手工取了一个实参地址,传给了形参引用(常量指针)。

5. 引用的细节问题

    看下面的代码:
#include <iostream>
using namespace std;

int myf01()
{
    int a;
    a = 11;
    return a;
}
int& myf02()
{
    int a;
    a = 11;
    return a;
}

int& myf03()
{
    int b;
    b = 22;
    return b;
}

void main()
{
    int b1 = myf01();
    printf("b1 = %d \n", b1);
    int b2 = myf02(); //返回一个值,然后赋给b2
    /**
    还原现场是这样的:
    b2 = p(p是a的别名),因此实现了赋值操作,执行完了这一步myf02()里面的变量才被清理
    */
    printf("b2 = %d \n", b2);
    int &b3 = myf03(); //b3是返回时的引用
    /**
    b3只是一个引用相当于int * const b3
    b3并没有记录下a的值,只记住了地址,函数运行完成了之后myf03()里面的局部变量被清理了(也可能没有),有可能会出错
    */
    printf("b3 = %d \n", b3); //b3是一个引用,c++编译器会帮我们执行*p操作
    system("pause");
}
/**
结果如下:
b1 = 11
b2 = 11
b3 = 22
    其实b3大多时候应该是一个未知的数,不过本人的64位机器上运行正常,这是因为还没有清理内存的缘故。
*/
    如果函数里面的值生命周期比较长,结果会有所不同:
#include <iostream>
using namespace std;

int& myj01()
{
    static int a; //a分配在全局区,生命周期和程序一致
    a = 10;
    return a;
}

void main()
{
    int b1 = myj01();
    printf("b1 = %d \n", b1);
    int b2 = myj01(); //返回一个值,然后赋给b2
    printf("b2 = %d \n", b2);
    int &b3 = myj01(); //b3是返回时的引用
    printf("b3 = %d \n", b3); //b3是一个引用,c++编译器会帮我们执行*p操作
    system("pause");
}
/**
结果如下:
b1 = 10
b2 = 10
b3 = 10
原因也很简单,那就是函数myj01运行完后a并未被清理掉,因此不会出错。
*/
    当然,也可以这样:
#include <iostream>
using namespace std;


int& myA(int &a)
{
    a++;
    return a;
}
void main()
{
    int b = 10;
    int b1 = myA(b);
    int b2 = myA(b);
    int b3 = myA(b);

    printf("b1 = %d \n", b1);
    printf("b2 = %d \n", b2);
    printf("b3 = %d \n", b3); //b3是一个引用,c++编译器会帮我们执行*p操作
    system("pause");
}
/**
结果如下:
b1 = 11
b2 = 12
b3 = 13
解释和前面的例子是一样的。
*/
    我们需要特别注意下面的代码:
#include <iostream>
using namespace std;

struct AdvTeacher
{
    char name[32];
    int age;
};

void getTeacher01(AdvTeacher **p)
{
    AdvTeacher *ptmp = (AdvTeacher *)malloc(sizeof(AdvTeacher));
    ptmp->age = 30;
    *p = ptmp;
}

//这个是结构体变量指针的引用,指针的引用
//p是t2的别名
void getTeacher02(AdvTeacher *&p) //和上面函数的作用一致
{
/**
&p <--> * const p  
*/
    p = (AdvTeacher *)malloc(sizeof(AdvTeacher));
    p->age = 30;
}

//如果不加引用,那么myT3会拷贝给p,修改p和myT3没有任何关系
//加了引用,p是myT3的别名,修改p和修改myT3一样
void  getTeacher03(AdvTeacher &p) //和上面函数的作用一致
{
    p.age = 11;

}
void main()
{
    AdvTeacher *t1 = NULL;
    AdvTeacher *t2 = NULL;
    getTeacher01(&t1);
    getTeacher02(t2);

    {
        AdvTeacher myT3;
        myT3.age = 10;
        getTeacher03(myT3);
    }

    system("pause");
}
    引用可以做左值:
#include <iostream>
using namespace std;

//static int a = 10;将a变成一个状态变量
//a初始化的时候为10,只会初始化一次
int& myg()
{
    static int a = 10; //分配在全局区
    printf("a = %d\n", a);
    return a;
}
void main()
{
    myg() = 11; //引用当做左值
    myg();
    system("pause");
}
/**
结果如下:
a = 10
a = 11
*/
    常引用:
    + 常引用 Const int &e  相当于 const int * const e
    + 普通引用 int &e 相当于 int *const e
    有两种方法对常引用进行初始化:
    + 让变量初始化const引用如
    int a = 10;
    const int &b = a;
    + 使用字面量初始化const引用
    const int &c = 10;
    虽然这在普通引用里不行,但在常引用里面行得通。当使用常量(字面量)对const引用进行初始化时,C++编译器会为常量值分配空间,并将引用名作为这段空间的别名,使用常量对const引用初始化后将生成一个只读变量。
#include <iostream>
using namespace std;

struct AdvTeacher
{
    char name[32];
    int age;
};

//const &
void  getTeacher(const AdvTeacher &p) //和上面函数的作用一致
{ //只能读,不能写
    //p.age = 11; //常引用不能被修改
    printf("age = %d \n", p.age);
}
//1. const引用的作用,让变量所指向的内存空间只读
//2. 给const引用初始化,有两种方法
//让变量初始化const引用
void main()
{
    //int &a = 10; //10没法取地址,因此会出错
    int b = 10;
    const int &c = b;
    //c = 11; //不能通过c去间接修改b
    b = 12; //可以修改b的值

    //const引用的第二种初始化方法
    const int &d = 10; //对10另外分配一块内存空间
    printf("&d = %d\n", &d);
    system("pause");
}
/**
结果因人而异,不过肯定的是10分配了空间
&d = 1506876
*/

6. c++引用需要注意的地方

    当函数返回值为引用时
    + 若返回栈内变量的引用,则不能用其成为其它引用的初始值,且不能将其作为左值使用。
    + 若返回静态变量或全局变量,可以成为其他引用的初始值,即可作为右值使用,也可作为左值使用。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值