zoukankan      html  css  js  c++  java
  • C++基础学习4:引用

    C++引用(Reference)
    
    引用(Reference)是C++语言相对于C语言的又一个扩充,是C++常用的一个重要内容之一。类似于指针,只是在声明的时候用"&"取代了"*"。正确、灵活地使用引用,可以使程序简洁、高效。我在工作中发现,许多人使用它仅仅 是想当然,在某些微妙的场合,很容易出错,究其原由,大多因为没有搞清本源。故在本篇中我将对引用进行详细讨论,希望对大家更好地理解和使用引用起到抛砖 引玉的作用。
    
    引用
        引用是别名,在声明时必须初始化,在实际代码中主要用作函数的形参
    
    (1)&在此不是求地址运算,而是起标识作用。
    (2)类型标识符是指目标变量的类型。
    (3)声明引用时,必须同时对其进行初始化。
    (4)引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,且不能再把该引用名作为其他变量名的别名。
    ra=1; 等价于 a=1;
    (5)声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。故:对引用求地址,就是对目标变量求地址。&ra与&a相等。
    (6)不能建立数组的引用。因为数组是一个由若干个元素所组成的集合,所以无法建立一个数组的别名。
    
    二、引用应用(请参见C++引用专题)
        1.引用作为参数
        2.常引用
        3.引用作为返回值
        4.引用和多态
    
    引用可以看做是被引用对象的一个别名,在声明引用时,必须同时对其进行初始化。引用的声明方法如下:
        类型标识符 &引用名 = 被引用对象
    
        C++引用示例: 
        
        int a = 10;
        int  &b = a;
        cout << a << " " << b << endl;
        cout << &a << " " << &b << endl;
        
        在本例中,变量b就是变量a的引用,程序运行结果如下:
            10       10
         0x64fe68 0x64fe68
         
    从这段程序中我们可以看出变量a和变量b都是指向同一地址的,也即变量b是变量a的另一个名字,也可以理解为0x64fe68空间拥有两个名字:a和b。由于引用和原始变量都是指向同一地址的,因此通过引用也可以修改原始变量中所存储的变量值,如例2所示,最终程序运行结果是输出两个20,可见原始变量a的值已经被引用变量b修改。
    [例2]通过引用修改原始变量中的值: 
        int a = 10;
        int &b = a;
        b = 20;
        cout << a << " " << b << endl;
        
        
    如果我们不希望通过引用来改变原始变量的值时,我们可以按照如下的方式声明引用:
        const 类型标识符 & 引用名 = 被引用的变量名
    
    这种引用方式成为常引用。如例3所示,我们声明b为a的常引用,之后尝试通过b来修改a变量的值,结果编译报错。虽然常引用无法修改原始变量的值,但是我们仍然可以通过原始变量自身来修改原始变量的值,如例3中,我们用a=20;语句将a变量的值由10修改为20,这是没有语法问题的。
    
    [例3]不能通过常引用来修改原始值: 
    复制格式化新窗口
    
        int a = 10;
        const int &b = a;
        b = 20;   //compile error
        a = 20;
        
    通过例2,我们可以知道通过引用我们可以修改原始变量的值,引用的这一特性使得它用于函数传递参数或函数返回值时非常有用。 
    
    1) 函数引用参数
    如果我们在声明或定义函数的时候将函数的形参指定为引用,则在调用该函数时会将实参直接传递给形参,而不是将实参的拷贝传递给形参。如此一来,如果在函数体中修改了该参数,则实参的值也会被修改。这跟函数的普通传值调用还是有区别的。
    
    
    C语言函数的参数传递
        按值传递,按值传递如果传递很大的数据项,赋值数据将导致较长的执行时间
    
    C++
        按引用传递,避免复制大量数据的开销,可以提高性能
        
    引用和指针的差别
        指针是个变量,可以把它再赋值成指向别处的地址
        建立引用时必须进行初始化并且绝不会在关联其他不同的变量
        由于指针也是变量,所以可以有指针变量的引用
    
        int *a = nullptr;
        int * &ptr = a;           //表示int*的引用ptr初始化为a
        int b = 8;
        ptr = &b;                 //ok, ptr是a的别名,是一个指针
        
        void &a = 3;         ---------          注意这是不合法的
        void只是在语法上相当于一个类型,本质上不是类型,但是没有任何一个变量或对象,其类型为void
        
        不能遍历引用的数组
        int a[10] = {0};
        int &ra[10]  =  a; //error不能建立一个引用类型的数组
        
        没有引用的指针和引用的引用
        int a;
        int &ra = a;
        int & *ptr = &ra;      // error企图定义一个引用的指针
        
        有空指针没有空引用,每一个引用都是有效的
        
        引用的参数传递
        传递引用给函数与传递指针的效果一样
        用引用作为参数比使用指针更有清晰的语法
        
        void swap(int &x, int &y);        // 引用作为参数
        
        void swap(int &x, int &y)         // 函数实现几乎和原来一样
        {
            int temp = x;
            x = y;
            y = temp;
        }
        
        void swap(int *x, int *y)
        {
            int z = *x;
            *x = *y;
            *y = z;
        }
        
        ==============================================================
        #include <iostream>
        using namespace std;
        
        void  foo(int val)
        {
            val = 10;
        }
        
        void bar(int &val)
        {
            val = 10;
        }
        
        void zoo(int *pval)
        {
            *pval = 10;
        }
        
        int main()
        {
            int a = 1;
            int b = 1; 
            int c = 1;
            foo(a);
            bar(b);
            zoo(&c);
            cout << a << " " << b << " " << c << endl;
            
            return 0;
        }
        
        ====================================================================
        
        引用的参数传递
        使用引用作为参数和返回值给函数的意义
            函数只能返回一个值。如果程序需要从函数返回两个值怎么办
            解决这个问题的办法之一是引用给函数传递两个参数,然后由函数往目标中填入正确的值
            
        函数返回值时,要生成一个值的副本。而引用返回值时,不生成值的副本,所以提高了效率。
        
        int result = 0;
        int  &func(int  r)      // 返回引用
        {
            result = r * r;
            return result;
        }
        
        注意:如果返回不在作用域内的变量或者对象的引用就有问题了。
        这与返回一个局部作用域指针的性质一样严重。
        
        int  &func(int r)
        {
            int result = 0;
            result = r * r;
            return result ;             // 返回局部变量的引用
        }
        
        int main()
        {
            int  &val  =  func(5);     // error返回的引用是个局部变量
            return 0;
        }
        
        =========================================================================
        
        此外,我们还需要注意一个小问题。如果我们将例5中的valplus函数定义成例6中所示的形式,那么这段程序就会产生一个问题,变量b的作用域仅在这个valplus函数体内部,当函数调用完成,b变量就会被销毁。而此时我们若将b变量的值通过引用返回拷贝给变量num2的时候,有可能会出现在拷贝之前b变量已经被销毁,从而导致num2变量获取不到返回值。虽然这种情况在一些编译器中并没有发生,但是我们在设计程序的时候也是应该尽量避免这一点的。
        
        在例4和例5中,我们就是为了避免这一点才采用的引用传递参数。普通的传值返回则不存在这样的问题,因为编译器会将返回值拷贝到临时存储空间后再去销毁b变量的。
        
        
        [例6]一个可能获取不到返回值的例子:     //因为b是局部变量,当程序结束以后,会自动销毁(堆栈)
        
        int & valplus(int a)
        {
            int b = a + 5;
            return b;
        }
        
        
  • 相关阅读:
    《C++标准程序库》 第6章 STL Container
    《C++语言99个常见编程错误》
    单例模式
    《C++标准程序库》 第7章 Iterator Adapters
    Shell颜色封装(C++)
    《改善C++程序的150个建议》
    OpenCV之图片的创建、保存和复制
    XMLDOM对象方法:对象事件
    三国中最精辟的十句话
    中国十大名茶及鉴别方法
  • 原文地址:https://www.cnblogs.com/ciyeer/p/8888600.html
Copyright © 2011-2022 走看看