C++中的引用

来源:互联网 发布:文字录入赚钱软件 编辑:程序博客网 时间:2024/05/02 05:06

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--->11a: 11b: 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 = 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(); //返回一个值,然后赋给b2    printf("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去间接修改b    b = 12; //可以修改b的值    //const引用的第二种初始化方法    const int &d = 10; //对10另外分配一块内存空间    printf("&d = %d\n", &d);    system("pause");}/**结果因人而异,不过肯定的是10分配了空间&d = 1506876*/

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

    当函数返回值为引用时    + 若返回栈内变量的引用,则不能用其成为其它引用的初始值,且不能将其作为左值使用。    + 若返回静态变量或全局变量,可以成为其他引用的初始值,即可作为右值使用,也可作为左值使用。
0 0
原创粉丝点击