您的位置:首页 > 编程语言 > C语言/C++

【三】C++引用机制

2015-08-14 16:52 316 查看

目录

目录
1变量名回顾

2引用的概念

3引用的意义

4const引用

5引用的本质

6函数返回引用

7C对三目运算符做了什么

8小结

1、变量名回顾

变量是一段实际连续存储空间的别名

程序中通过变量来申请并命名存储空间

通过变量的名字可以使用存储空间

如图:



思考:

  对于一段连续的存储空间只能有一个别名吗?

肯定不是,因此我们可以给同一个连续的存储空间取多个别名,这就诞生了C++中的引用机制

2、引用的概念

引用可以看作一个已定义变量的别名

引用的语法:Type& name = var;

示例:

exp-1.cpp

#include <stdio.h>

int main(int argc, char *argv[])
{
int a = 4;
int& b = a;

b = 5;

printf("a = %d\n", a);
printf("b = %d\n", b);
printf("&a = %p\n", &a);
printf("&b = %p\n", &b);

return 0;
}


运行结果:



可见,a和b指向了同一内存块!

Tip:

  普通引用在声明时必须用其它的变量进行初始化。

3、引用的意义

引用作为其它变量的别名而存在,因此在一些场合可以代替指针

引用相对于指针来说具有更好的可读性和实用性

示例:

exp-2.cpp

#include <stdio.h>

void swap_q(int& a, int& b)
{
int temp = a;
a = b;
b = temp;
}

void swap_p(int *pa, int *pb)
{
int temp = *pa;
*pa = *pb;
*pb = temp;
}

int main(int argc, char *argv[])
{
int a = 4;
int b = 5;

swap_q(a, b);
printf("a = %d\n", a);
printf("b = %d\n", b);

printf("******************************\n");

swap_p(&a, &b);
printf("a = %d\n", a);
printf("b = %d\n", b);

return 0;
}


Tip:

  引用作为函数参数声明时可不进行初始化。

4、const引用

在C++中可以声明const引用

const Type& name = var;

const引用让变量拥有只读属性

示例:

exp-3.cpp

#include <stdio.h>

int main(int argc, char *argv[])
{
int a = 4;
const int& b = a;
int* p = (int*)&b;

//b = 5; //这里编译器会报错,不能修改只读变量的值

*p = 5; //这里成功修改了变量a的值

printf("a = %d\n", a);
printf("b = %d\n", b);

return 0;
}


运行结果:



Tip:

  从运行结果来看,指向变量a的指针还是能成功的改变a的值,所以,此时只是无法通过常量引用b来修改a的值!

当使用常量对const引用进行初始化时,C++编译器会为常量值分配空间,并将引用名作为这段空间的别名

示例:

exp-4.cpp

#include <stdio.h>

int main(int argc, char *argv[])
{
const int& b = 1;       //编译器会为其分配存储空间
int* p = (int*)&b;

//b = 5;    //这里会报错,因为常量引用为只读

*p = 5;     //还是可以通过指针来更改其值

printf("b = %d\n", b);

return 0;
}


Tip:

  使用常量对const引用初始化后将生成一个只读变量。

经典问题:

#include <stdio.h>

int main()
{
int y = 10;
const int z = y; //试图用一个变量给一个常量赋值,来定义一个常量,但是这样做,z真的是一个常量么?
//验证方法:
//取z的地址,让编译器为它分配空间,然后把它的地址强制去掉底层const属性,通过指针向其地址空间赋值,最后看z的输出值是否改变,如果改变说名不是常量,是只读变量!

int *p = const_cast<int*>(&z);
*p = 7;

printf("z = %d\n", z);
printf("*p = %d\n", *p);

return 0;
}


运行结果:



从运行结果来看,上面的z是一个只读变量,而非常量!

#include <stdio.h>

int main()
{
char c = 'c';
char& rc = c;
const int& trc = c; //用一个变量给一个常引用赋值
//已经知道,如果 const int& i = 1; 编译器会为它分配存储空间,从而生成一个只读变量
//而这里也是一样的,trc最终会是一个只读变量

rc = 'a';

printf("c = %c\n", c);
printf("rc = %c\n", rc);
printf("trc = %c\n", trc);

return 0;
}


运行结果:



5、引用的本质

思考:

  引用有自己的存储空间吗?

测试:

exp-5.cpp

#include <stdio.h>

struct TRef
{
int& a;
int& b;
};

int main(int argc, char *argv[])
{

printf("sizeof(TRef) = %ld\n", sizeof(TRef));

return 0;
}


运行结果:



Tip:

  从上面的结果可知,引用是有存储空间的,那么这又是为什么呢?

- 引用在C++中的内部实现是一个指针常量

- Type& name <==> Type* const name

Improtance:

C++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小与指针相同。

从使用的角度,引用会让人误会其只是一个别名,没有自己的存储空间,这是C++为了实用性而做出的细节隐藏。

思考:

  既然引用变量有自己的存储空间,如何获得它的该空间的地址?

解答:

  无法通过“&”取地址符,获得“引用变量”自身的存储空间的地址,通过“&”取地址符得到的是它的目标变量的地址,引用机制在一开始设计时,就没打算让我们操作它本身,对它的所有操作都反应在目标变量上;而且,引用机制很容易被编译器优化,优化后,原来访问引用变量的代码编译成的机器代码,实际上是直接访问原变量,这样的话,编译器甚至可以不为引用变量分配空间,全部直接访问它所引用的原变量。

举例:

exp-6.cpp

#include <stdio.h>

struct TRef
{
int& a;
int& b;
int& c;
};

int main(int argc, char *argv[])
{
int a = 1;
int b = 2;
int c = 3;
TRef rA = {a, b, c};

printf("&a = %p\n", &a);
printf("&rA.a = %p\n", &rA.a);

printf("&b = %p\n", &b);
printf("&rA.b = %p\n", &rA.b);

printf("&c = %p\n", &c);
printf("&rA.c = %p\n", &rA.c);

printf("&rA = %p\n", &rA);
printf("sizeof(TRef) = %ld\n", sizeof(rA));

return 0;
}


运行结果:



Tip:

  可见,对引用变量取地址,实际上得到的是其目标变量的地址!

6、函数返回引用

若返回栈变量

不能成为其它引用的初始值

不能作为左值使用

若返回静态变量或全局变量

可以成为其他引用的初始值

即可作为右值使用,也可作为左值使用

示例:

exp-7.cpp

#include <stdio.h>

int& f()
{
static int a = 0;   //静态局部变量,函数退出后不释放

return a;
}

int& g()
{
int a = 0;      //局部变量,函数退出后释放

return a;
}

int main()
{
int a = g();
int& b = g();

f() = 10;   //C中编译不过,
//C++中f函数返回的引用可作为左值使用,这一点类似于三目运算符

printf("a = %d\n", a);
printf("b = %d\n", b);
printf("f() = %d\n", f());

return 0;
}


运行结果:



Tip:

  首先,g函数执行后,将局部变量的引用返回并赋值给了变量a,所以a的值为0;然后,g函数执行后,将局部变量赋值给一个引用变量b,然后由于g函数执行完毕,系统释放了调用g函数时的栈空间,导致b此时成为了一个未知内存块的引用,所以访问它得到的是随机值;最后,f函数执行后将它的静态局部变量的引用返回,然后作为左值给它赋值,所以值为10。

7、C++对三目运算符做了什么?

当三目运算符的可能返回都是变量时,返回的是变量的引用

当三目运算符的可能返回中有常量时,返回的是常量值

8、小结

C++中的引用可以看作变量的别名来使用

C++中的常引用可以使得一个变量拥有只读属性

C++中的常引用可以用常量初始化而得到一个只读变量

C++中引用的本质是一个指针常量
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: