c++的引用

1. 变量名

在引入引用之前,先回顾下c/c++中的变量名。所谓变量名,实际是一段实际连续存储空间的别名,程序中通过变量来申请并命名存储空间,因此,可以通过变量的名字可以使用存储空间。那么一段连续的存储空间是否只能有一个别名?肯定不是的,那么在c++中就引进引用的概念。

2. 引用

引用可以看作是一个已定义变量的别名,分为普通引用和特殊引用。

2.1 普通引用

普通引用其语法方式如下:

type& name = dat;

例如:

int x = 4;
int& rx = x;    //rx为x的别名
rx = 6;         //操作rx就是操作x

这就是一个普通引用的使用方式(下面讲解特殊引用)。普通引用既然是一个别名,那么在定义的时候必须用同类型的变量进行初始化,“引用了谁”这个必须在定义的时候初始化好。注意,普通引用只能引用同类型的变量。

int a = 4;
int& ra1 = a;  //正确
char& ra2 = a; //error
int& rr = 1;   //error
ra1 = 6;       //ra1 = 6, a = 6; &ral等于&a

另外,引用在函数的形参中可以不需要被初始化,因为程序在编译的时候编译器再初始化。

2.2 特殊引用

所谓特殊的引用即是指const引用,语法如下:

const type& name = dat;

const引用变量,使得变量具有只读属性,

int a = 5;
const int& b = a;   //这样声明使得b具有只读属性
int *p = (int* )&b;
int *q = (int* )&a;

b = 2;      //error,b具有只读属性
*p = 6;     //ok,修改变量a的值
a = 5;      //ok
*q = 5;     //ok

如上所示,const引用可以用同类型的变量初始化外,这点跟前面的普通引用初始化是一致的,而对于const引用,还可以:
(1)用同类型的常量来初始化

const int& a = 1;
int* p = (int* )&a;

//a = 5;    //a是const引用,具有只读属性,不可修改
*p = 8;     //ok
printf("&a = %p, p = %p\n", &a, p);

这里写图片描述
打印出了合法的地址值,这说明c++编译器会为常量分配空间,并将引用名作为这段空间的别名。
(2)用不同类型的变量来初始化

char c = 'a';
char& rc = c;         //用c初始化普通引用rc
const char& mrc = c;  //用c初始化相同类型的const引用
const int& trc = c;   //用c初始化不同类型的const引用

rc = 'h';             //通过普通rc引用修改变量c
printf("c = %c, rc = %c, mrc = %c, trc = %c\n", c, rc, mrc, trc);
printf("&c = %p, &rc = %p, &mrc = %p, &trc = %p\n", &c, &rc, &mrc, &trc);

编译运行:
这里写图片描述
相同类型的const引用mrc的地址和c的地址相同(打印的的内容也一致),说明二者是同一份内存空间的。
不同类型的const引用trc的地址和c的地址不同(打印的的内容不一致),说明二者是不同的内存空间的。
结论:
1)mrc和c共享同一份内存空间,只是mrc该变量具有了只读属性。
2)编译器为trc另外分配了一块空间,并且该空间的值为c变量的值,同样具有只读属性

2.3 引用的本质就是指针常量

指针是一个常量,它的值是一个内存地址,不需要被初始化,它可以保存不同的地址。通过指针可以访问对应的内存地址中的值。指针可以被const申明,使之成为常量或者只读变量。

const指针常量: int* const p;            //指向的地址不可更改
const只读变量: const int* p = "xxx";        //指针指向的内存空间上的内容不可更改

引用是一个变量的新名字,对引用的操作(取址、赋值)都会被传递到代表的变量来。const引用使其代表的变量具有只读属性。引用必须在定义的时候初始化,之后无法在代表其他变量。对程序员来说,引用与指针没有任何关系,引用是变量的新名字,也就是一段内存空间的别名,操作引用就是操作对应的变量;但对编译器来讲,其内部是使用指针常量来实现引用的,既然是常量,那么在定义额定时候就必须要初始化。

2.4 c++中不允许出现引用数组

所谓引用数组,就是数组的每一个元素都是变量的引用,先看一每个元素都是引用的结构体的使用:

int a = 1;  //a是全局变量

//声明一个结构体,其成员都是int型引用
struct TT{
    int& x;
    int& y;
    int& z;
};

int main(void)
{
    int b = 2;          //b是局部变量
    int* c = new int(3);//c是动态分配在堆的变量
    TT tt = {a, b, *c}; //定义一个结构体tt,将其成员初始化为上面三种不同地址的元素的引用

    printf("tt.x = %d, tt.y = %d, tt.z = %d\n", tt.x, tt.y, tt.z);
    printf("&tt.x = %p, &tt.y = %p, &tt.z = %p\n", &tt.x, &tt.y, &tt.z);
    delete c;
    return 0;
}

编译运行:
这里写图片描述
可见x、y、z位于不同的内存空间。上面的TT结构体中的元素都是int型引用,这个特点跟数组一样,那么将TT结构体替换成数组呢?

int a = 1;  //a是全局变量
int main(void)
{
    int b = 2;                      //b是局部变量
    int* c = new int(3);            //c是动态分配在堆的变量
    int& my_array[] = {a, b, *c};   //定义一个引用数组,其成员都是int型变量的引用。(c++不允许引用数组的存在)

    return 0;
}

编译:
这里写图片描述
原因在于,在c++语言中不允许出现引用数组。所谓引用数组就是数组中的每个成员都是一种类型的引用。如上, my_array[0] 等于a的引用, my_array[1]等于b的引用…引用该变量就是该变量所在地址空间的别名,那么其地址就等于该变量的地址,而a、b、c的地址是不连续的。在c语言中的数组中的特性,数组的间的间隔必须是连续的,在这里,&my_array[1] - &my_array[0]应该等于sizeof(int),也就是4字节,而事实相减后并非如此。c++为了兼容c语言中的数组,不得不放弃引用数组。c++不允许引用数组的存在,那么也就意味着,数组在传参的时候也不能使用引用数组,如下,

void test_func(int& array[3])  //想要声明一个引用数组,报错
{
}
int main(void)
{
    int main_array[3] = {1, 2, 3};
    test_func(main_array);
    return 0;
}

引用数组中的元素是来自各个变量的内存空间的值,对应的其地址也五花八门,所以c++为了兼容c语言中数组空间是连续的,不允许出现引用数组这个玩意,所以上面的test_func(int& array[3])出错。若把test_func()函数原型修改为

void test_func(int (&array)[3]);

这样编译得以通过,这是因为array并非是一个引用数组,而是一个int型指针变量的引用,它不是数组,只是它引用是数组名main_array。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值