美文网首页
(六)C++篇-函数非引用形参和引用形参

(六)C++篇-函数非引用形参和引用形参

作者: GoodTekken | 来源:发表于2022-06-17 08:30 被阅读0次

    (1)非引用形参
    普通的非引用类型的参数通过复制对应的实参实现初始化。当用实参副本初始化形参时,函数并没有访问调用所传递的实参本身,因此不会修改实参的值

    // return the greatest common divisor
    int gcd(int v1, int v2)
    {
    while (v2) {
    int temp = v2;
    v2 = v1 % v2;
    v1 = temp;
    }
    return v1;
    }
    

    (2)指针形参 void reset(int *ip)
    函数的形参可以是指针,此时将复制实参指针。与其他非引用类型的形参一样,该类形参的任何改变也仅作用于局部副本。如果函数将新指针赋给形参,主调函数使用的实参指针的值没有改变。
    事实上被复制的指针只影响对指针的赋值。如果函数形参是非 const 类型的指针,则函数可通过指针实现赋值,修改指针所指向对象的值:

    #include <iostream>
    using namespace std;
    void reset(int *ip);
    int main()
    {
        int i = 42;
        int *p = &i;
        cout << "i: " << *p << '\n';  // prints i: 42
        reset(p); // changes *p but not p
        cout << "i: " << *p << endl; // ok: prints i: 0
        return 0;
    }
    
    void reset(int *ip)
    {
    //(二选一)*ip=0,可以改变实参的值
        *ip = 0; // changes the value of the object to which ip points
        cout << "in_i: " << ip << endl; //print: in_i: 0x7ffde64b447c
    //(二选一)ip=0,将指针地址改成0,但不可以改变实参的值
        //ip = 0;  // changes only the local value of ip; the argument is unchanged
        //cout << "in_i: " << ip << endl; //print: in_i: 0
    }
    

    如果保护指针指向的值(保护实参不被改变),则形参需定义为指向 const 对象的指针:

    void reset(const int *ip)
    {
        *ip = 0;// error: assignment of read-only location ‘* ip’
    }
    

    指针的初始化规则: 可以将指向 const 对象的指针初始化为指向非 const
    对象,但不可以让指向非 const 对象的指针向 const 对象。

    //ok
    int i = 42;
    const int *p = &i;
    
    //fail
    const int i = 42;
    int *p = &i;
         // |  int *p = &i;
         // |           ^~
        //  |           |
        //  |           const int*
    
    

    复制实参的局限性
    复制实参并不是在所有的情况下都适合,不适宜复制实参的情况包括:
    • 当需要在函数中修改实参的值时。
    • 当需要以大型对象作为实参传递时。对实际的应用而言,复制对象所付出的时间和存储空间代价往往过大。
    • 当没有办法实现对象的复制时。
    对于上述几种情况,有效的解决办法是将形参定义为引用或指针类型

    (3)引用形参
    为了使 swap 函数以期望的方式工作,交换实参的值,需要将形参定义为引
    用类型(int &v1, int &v2)
    引用形参直接关联到其所绑定的对象,而并非这些对象的副本。定义引用时,必须用与该引用绑定的对象初始化该引用。引用形参完全以相同的方式工作。每次调用函数,引用形参被创建并与相应实参关联。
    形参 v1 只是对象 i 的另一个名字,而形参v2 则是对象 j 的另一个名字。对v1 的任何修改实际上也是对 i 的修改。同样地,对v2 的任何修改实际上也是对 j 的修改。

    引用形参的另一种用法是向主调函数返回额外的结果。

    测试代码如下:

    #include<iostream>
    using namespace std;
    
    void swap(int &v1, int &v2);
    int main()
    {
        int i = 10;int j = 20;
        cout << "Before swap():\ti: "<< i << "\tj: " << j << endl;
        cout << "i:\ti: "<< &i << endl;
        swap(i, j);
        cout << "After swap():\ti: "<< i << "\tj: " << j << endl;
        cout << "i:\ti: "<< &i << endl;
        return 0;
    }
    
    // ok: swap acts on references to its arguments
    void swap(int &v1, int &v2)
    {
        int tmp = v2;
        v2 = v1;
        v1 = tmp;
        cout << "v1:\ti: "<< &v1 << endl;
    }
    

    输出结果

    tekken@tekken:~/C++WS$ ./a.out 
    Before swap():  i: 10   j: 20
    i:  i: 0x7ffc06cccef0
    v1: i: 0x7ffc06cccef0
    After swap():   i: 20   j: 10
    i:  i: 0x7ffc06cccef0
    

    使用 const 引用就可避免复制:

    // compare the length of two strings
    bool isShorter(const string &s1, const string &s2)
    {
          return s1.size() < s2.size();
    }
    

    (1)因为形参是引用,所以不复制实参。又因为形参是 const 引用,所以 isShorter 函数不能使用该引用来修改实参。
    (2)非 const 引用形参只能与完全同类型的非 const 对象关联。

    字面型常量参数对应const形参
    变量可以对应引用形参
    值得注意,如果string word = "Hello World";改成const string word = "Hello World";,由于类型不一致,同样会编译失败。
    测试代码:

    #include<iostream>
    #include<string>
    using namespace std;
    
    string::size_type find_char(const string &s, char c);
    string::size_type find_char_2(string &s, char c);
    int main()
    {
        string::size_type i = find_char("Hello World", 'o');//(1)字面型常量参数对应const形参
        cout<<"i: "<<i<<endl;
        string word = "Hello World";
        //const string word = "Hello World";  //使用不一致的参数类型会导致编译失败
        string::size_type i2 = find_char_2(word, 'o');      //(2)变量可以对应引用形参
        cout<<"i2: "<<i2<<endl;
        return 0;
    }
    
    // returns index of first occurrence of c in s or s.size() if c isn't in s
    // Note: s doesn't change, so it should be a reference to const
    string::size_type find_char(const string &s, char c)
    {
        string::size_type i = 0;
        while (i != s.size() && s[i] != c)
        ++i; // not found, look at next character
        return i;
    }
    
    string::size_type find_char_2(string &s, char c)
    {
        string::size_type i = 0;
        while (i != s.size() && s[i] != c)
        ++i; // not found, look at next character
        return i;
    }
    

    输出:

    tekken@tekken:~/C++WS$ ./a.out 
    i: 4
    i2: 4
    

    传递指向指针的引用
    假设我们想编写一个与前面交换两个整数的 swap 类似的函数,实现两个指针的交换。已知需用 * 定义指针,用 & 定义引用。现在,问题在于如何将这两个操作符结合起来以获得指向指针的引用。这里给出一个例子:
    (形参int *&v1的定义应从右至左理解:v1 是一个引用,与指向 int 型对象的指针相关联。也就是说,v1 只是传递进 ptrswap 函数的任意指针的别名。)
    最后是指针的值被交换了。在调用 ptrswap 时,pi 指向 i,而 pj 则指向 j。
    在 ptrswap 函数中, 指针被交换, 使得调用 ptrswap 结束后, pi 指向了原来 pj所指向的对象。换句话说,现在 pi 指向 j,而 pj 则指向了 i。
    测试代码如下:

    #include<iostream>
    using namespace std;
    
    void ptrswap(int *&v1, int *&v2);
    int main()
    {
        int i = 10;
        int j = 20;
        cout<<"i:"<<&i<<endl;
        cout<<"j:"<<&j<<endl;
        
        int *pi = &i; // pi points to i
        int *pj = &j; // pj points to j
        cout<<"pi:"<<pi<<endl;
        cout<<"pj:"<<pj<<endl;
        
        cout << "Before ptrswap():\t*pi: "<< *pi << "\t*pj: " << *pj << endl;
        ptrswap(pi, pj); // now pi points to j; pj points to i
        cout << "After ptrswap():\t*pi: "<< *pi << "\t*pj: " << *pj << endl;
        
        cout<<"pi:"<<pi<<endl;
        cout<<"pj:"<<pj<<endl;
        return 0;
    }
    
    // swap values of two pointers to int
    void ptrswap(int *&v1, int *&v2)
    {
        int *tmp = v2;
        v2 = v1;
        v1 = tmp;
    }
    

    输出结果:

    tekken@tekken:~/C++WS$ ./a.out 
    i:0x7ffe11831990
    j:0x7ffe11831994
    pi:0x7ffe11831990
    pj:0x7ffe11831994
    Before ptrswap():   *pi: 10 *pj: 20
    After ptrswap():    *pi: 20 *pj: 10
    pi:0x7ffe11831994
    pj:0x7ffe11831990
    

    相关文章

      网友评论

          本文标题:(六)C++篇-函数非引用形参和引用形参

          本文链接:https://www.haomeiwen.com/subject/jsnfvrtx.html