@lishuhuakai
2015-05-19T15:39:21.000000Z
字数 4351
阅读 2019
c++
+ 变量名实质上是一段连续存储空间的别名,是一个标号
+ 程序中通过变量来申请并命名内存空间
+ 通过变量的名字可以使用存储空间
引用是C++的概念,属于C++编译器对C的扩展。
int main(){int a = 0;int &b = a; //int * const b = &ab = 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--->11a: 11b: 11&a: 3798852&b: 3798852*/
结论:1. 普通引用在声明时必须用其它的变量进行初始化
为什么必须初始化? --> 结论:很像一个只读的常量
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);}
普通引用有自己的内存空间吗?回答是肯定的:
#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++编译器帮我们程序员手工取了一个实参地址,传给了形参引用(常量指针)。
看下面的代码:
#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 b3b3并没有记录下a的值,只记住了地址,函数运行完成了之后myf03()里面的局部变量被清理了(也可能没有),有可能会出错*/printf("b3 = %d \n", b3); //b3是一个引用,c++编译器会帮我们执行*p操作system("pause");}/**结果如下:b1 = 11b2 = 11b3 = 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(); //返回一个值,然后赋给b2printf("b2 = %d \n", b2);int &b3 = myj01(); //b3是返回时的引用printf("b3 = %d \n", b3); //b3是一个引用,c++编译器会帮我们执行*p操作system("pause");}/**结果如下:b1 = 10b2 = 10b3 = 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 = 11b2 = 12b3 = 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 = 10a = 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去间接修改bb = 12; //可以修改b的值//const引用的第二种初始化方法const int &d = 10; //对10另外分配一块内存空间printf("&d = %d\n", &d);system("pause");}/**结果因人而异,不过肯定的是10分配了空间&d = 1506876*/
当函数返回值为引用时
+ 若返回栈内变量的引用,则不能用其成为其它引用的初始值,且不能将其作为左值使用。
+ 若返回静态变量或全局变量,可以成为其他引用的初始值,即可作为右值使用,也可作为左值使用。