引用(别名)

一、引用

1.引用的定义

        类型 & 引用变量名称 = 变量名称

        &和类型结合称之为引用符号,不是取地址的符,代表别名意思。

        引用分为左值引用、右值引用。

2.左值、右值、将亡值(亡值)

        左值:可以取地址的变量

        右值:不能取地址的变量

        将亡值:在表达式的计算过程产生,计算结束则消失(并不一定在计算过程产生)

int main()
{
    int a=10;
    int b=a;
    int &c=a;    //c为a的别名,在c++11中称为左值引用
    int &&d=10;  //此时为右值引用,并且这时的d可以被左值引用,即int &p=d;
}

                 

c为a的引用,与a在同一个地址,b虽然与a的值一样,但是不在同一地址。

二、引用的特点

1.定义引用必须初始化

2.没有空引用

3.没有引用的引用,即所谓的二级引用

int main()
{
    int a=10;
    int &x;        //err
    int &y=NULL;   //err 
    int &b=a;        
    int &&c=b;     //err    注意区分它和右值引用的区别
}

三、const引用

int main()
{
    int a=10;
    const int b=20;//b为常性值,只读不写
    int &x=b;      //x可读可写
    const int &x=b;   
    const int &y=a;  
    const int &z=10;
    return 0;
    
}

const引用(万能引用),可以引用左值,也可以引用右值

四、引用作为形参替代指针

引用比指针更加安全,但引用没有指针灵活(引用不可改变对象)

引用可以对实参进行改变,而指针不可,且引用不存在NULL引用,不需要判空,比指针安全

指针占地址空间,而引用不占(或者说是引用占地址空间,但系统忽略,认为它不占)

//C语言的实现
void my_swap(int *ap,int *bp)
{
    assert(ap!=NULL&&bp!=NULL);
    int tmp = *ap;
    *ap=*bp;
    *bp=tmp;
}
int main()
{
    int a=10,b=10;
    cout<<"a ="<<a<<"b = "<<b<<endl;
    my_swap(&a,&b);
    cout <<"a="<<a<<"b="<<b<<endl;
    return 0;
}


//c++的实现
void my_swap(int &x,int &y)
{
   int tmp=x;
    x=y;
    y=tmp;
}
int main()
{
    int a=10,b=10;
    cout<<"a ="<<a<<"b = "<<b<<endl;
    my_swap(a,b);
    cout <<"a="<<a<<"b="<<b<<endl;
    return 0;
}

五、其他引用形式

int main()
{
    int a=10,b=20;
    int ar[5]={1,2,3,4,5};
    int *p=&a;
    int *&rp=p;    //引用指针   rp为p的别名
    int &x=ar[0];
    int (&br)[5]=ar; //引用数组(此时的ar是数组的整体,而不是数组首地址) br为ar的别名
}

注意:只有引用指针、引用数组,而无指针引用、数组引用

int &br[10];    //可以实现吗?
    不可,我们在引用之前是无法知道被引用对象的类型的,而要实现int &br[10]我们必须定义类型,故无法实现。

int &*p;        //可以实现吗?
    不可,引用时,我们必须要进行初始化,而指针可以不进行初始化,互相冲突,故不可实现,且引用不可改变引用对象,但指针可以改变指向对象,互相冲突。

六、引用与指针的区别

 1.指针变量存储某个实例(变量或对象)的地址,引用是某个实例的别名

2.程序为指针变量分配内存区域,而不为引用分配内存区域

3.解引用是指针使用时要在前加" * ",引用可以直接使用

4.指针变量的值可以发生改变,存储不同实例的地址;引用在定义时就被初始化,之后无法改变(不能是其他实例的引用)

5.指针变量的值可以为空(NULL,nullptr),没有空引用

6.指针变量作为形参时需要检测它的合法性(判空),引用不需要判空

7.对指针变量使用“sizeof”的到的是指针变量的大小(指针解引用"sizeof"得到的是变量的大小),而对引用变量使用“sizeof”得到的是变量的大小

8.理论上指针的级数没有限制,但引用只有一级,即不存在引用的引用,但可以有指针的指针

9.++引用与++指针变量的效果不一样

        对指针变量的操作,会使指针变量指向下一个实体(变量或对象)的地址,而不是改变所指实体(变量或对象)的内容

        对引用的操作直接反应到所引用的实体(变量或对象)

int ar[10]={12,23,34,45,56,67,78,89};
int *ip = &ar[1];    //*ip=23
int &ra = ar[1];     //ra=23
++*ip;   //ar=24    23+1
++ra;    //ra=24    23+1
++ip;    //*ip=34

七、引用的本质

从汇编层次理解引用与指针变量的区别

        编译器在编译过程中,会将引用变为指针,并且引用对于原码来说没有内存空间,在机器码上有内存空间。

//原码
int main()
{
    int a=10;
    int *ip=&a;
    int &b=a;    //int * const b = &a;(机器码)
    *ip = 100;
    b = 200;
    return 0;
}

为什么int &b变为int * const b?

        答:引用在定义时就初始化,不可发生改变,当int &b=a时,则b只能为a的别名,但指针变量的值是可以改变的,故const是为使b只能指向a,不可发生改变。

引用本质上为指针(* const)

八、引用作为函数的返回值类型

不可以对函数中的局部变量或对象以引用或指针方式返回

//在函数结束后,内存空间将还给系统,因此不可返回a的地址
//若要返回a的地址,将a设为全局变量或静态变量,即生存期不受函数的影响
int * func_1()
{
    int a=10;
    return &a;
}


//在机器码中,引用最终还是变为指针,return &a;
int & func_2()
{
    int a=10;
    return a;
}
//以上两种都是错误的

引用使用的变量不受函数的影响,就可返回变量的地址。

        在函数结束,其空间还给栈区,但数据区不受函数影响,而全局变量和静态在数据区,因此这两者可以返回变量的地址。

九、分析效率

struct Student
{
    char s_id[20];
    char s_name[20];
    char s_sex[8];
    int s_age;
};

void funa(struct Student sx)    //传值(效率低),不需要定义变量
{
    //在funa中,对sx进行改变,不会影响s1
}

void funa(struct Student *ps)    //传地址,需要定义变量
{
    //使用指针必须判空,ps改变,会改变S1
}

void funa(struct Student &st)    //引用,传地址,需要定义变量
{
    //st改变,会改变s1
    //不用判空,但灵活性差
}

int main()
{
    struct Student s1={};
    funa(s1);    //传52个字节
    funb(&s1);   //传4个字节 
    func(s1);    //传4个字节
    return 0;
}

  • 18
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值