C++中的函数参数传递


在函数调用的时候,parameters被创建和初始化,初始化操作使用传递来的arguments完成。

Arguments are the initializers for a function’s parameters.

这里先要理解两个概念,对于一个func函数:

int func(int a){
    return a
}

int b = 0;
func(b);
  • a称为parameter
  • b称为argument

parameters的初始化与变量的初始化过程相同,因此在函数初始化中会遇到:

  • 直接copy数据
  • 绑定到argument的reference

这两种方式也分别对应两种函数传递方式:

  • pass arguments by value
  • pass arguments by reference

Pass arguments by value

基本使用

这种参数传递方式,有以下2个特点:

  • 传入函数内的argument会被copy为parameter;
  • 在函数内对于copy的参数parameter的写操作,不影响外部的argument;

这种函数传递方式是最简单的一种,例如上述的func(int a)就是这种参数传递类型。

pointer作为函数参数

按理说,pointer类型的函数参数与其他的类型没啥不同,但是由于pointer指向一个变量的地址,因此会有一些不同的玩法,导致起到和pass arguments by reference的效果。

void func(int *ip) {
    *ip = 100;
    ip = nullptr;
}

int b = 10;
func(&b);

执行上述代码后,b = 100, ip变成空指针,但是不影响b的地址。而通过,更改 *ip可以起到按引用传递的效果。

但是在C++中更推荐使用下文中的pass arguments by reference,而不是这种指针的用法。

Pass arguments by reference

这种参数传递方式的特点是:

  • 允许在函数内部改变argument中的数据;
  • 不需要进行copy操作;

用reference作为参数避免copy

函数传递中的问题:

It can be inefficient to copy objects of large class types or large containers. More-over, some class types (including the IO types) cannot be copied.

针对该问题,使用按引用传值,避免了资源不必要的copy。

bool isShorter(string &s1, string &s2) {
    return s1.size() < s2.size(); 
}

用reference作为参数返回更多信息

函数传递中的另一个问题:

A function can return only a single value. However, sometimes a function has more than one value to return

利用按引用传递,可以返回函数内部的多个操作结果,一般有两种方式:

  • 一个是定义多个parameters;
  • 另一个是定义数据结构,如结构体、类等;

const Parameters & Arguments

top-level const的问题

在之前关于const的总结中提到了top-level const和low-level const:

  • top level const:对象自己是const;
  • low level const:对象指向的对象是const;

在函数参数传递时,关于top-level const有几点注意事项:

  • top-level const on parameters are ignored.

    void func(const int i){
        return i;
    }
    
    const int a = 1;
    int b = 2;
    
    func(a);  // ok
    func(b); // ok, top-level const is ignored.
    
  • 虽然top-level const被忽略了,但是在func中不能修改ii;

    void func(const int ii){
        ii = 100;  // error
    }
    // error: assignment of read-only parameter ‘ii’
    
  • top level const不影响函数类型,在函数指针的总结中提到了函数类型,由参数类型和返回值类型决定,因此相同函数名也视为不同的函数,但是top level const可以被忽略,因此不会对函数参数类型产生影响;

    void func1(const int i){}
    void func1(int i){}
    
    // error: redefinition of ‘void func1(int)’
    

    因此,上述两个func1不是函数重载,而是完全一样。

当const遇到Pointer & Reference

既然函数参数的初始化与变量的初始化一致,因此也有一些通用的指导规则:

  • 对于有low level const的对象,可以通过非const对象来初始化,但是反过来不行;
  • 引用类型的parameters初始化必须由相同类型的arguments完成;
void func(int &r){}
void func(int *p){}

// for every call in the following
int i = 0;
const int ci = i;

string::size_type ctr = 0;

func(&i);  // ok,调用2th func
func(&ci); // error, 不能使用pointer to const int初始化 pointer to int,违反了low-level const的原则

func(i);   // ok,,1st func
func(ci);  // error,不能使用const int初始化plain reference
func(42);  // error, 不能使用literal初始化plain reference
func(ctr); // error, mismatched type

推荐:使用referecne to const作为函数parameters

在函数参数中使用按引用传递参数的方式,可能会导致以下问题:

  • 暗示函数调用者,可以改变该引用绑定的值,引起错误;
  • 限制了能传入到该参数中的arguments的类型;
    如上例所示,调用 func(int &r)时:
    • 不能传入42等字面值;
    • const int类型;
    • 或者size_type等可以自动转换的类型;

      出现:error: cannot bind non-const lvalue reference of type ‘int&’ to an rvalue of type ‘int’
      基于上述问题,因此在函数传递时,推荐使用reference to const。

void func(const int &i){}

参考资料

  1. C++ Primer - 6.2.1 Passing Arguments by Value
  2. C++ Primer - 6.2.2 Passing Arguments by Reference
  3. C++ Primer - 6.2.3 const Parameters and Arguments

文章作者: alex Li
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 alex Li !
  目录