详谈C++函数参数传递

详谈C++函数参数传递


说明:

  • 在C++中,依旧可以使用C语言的Pass By Pointer(指针传递)方式,但是在C++建议使用Pass By Reference替代Pass By Pointer,所以没有考虑Pass By pointer;
  • 有关三种传递方式的介绍,可以参照C++中的值传递、引用传递和指针传递
  • 本文只探讨Pass By Value , Pass By Reference两种传递方式。

0x01 参数传递

首先我们要知道的是每次函数调用时都会重新创建它的形参,并用传入的实参对形参进行初始化

// 定义函数
int great(int a, int b) {
    return a > b ? a : b;
}
// 函数调用
great(3, 5);   // 调用1
great(10, 20); // 调用2

上面的函数有两个形参ab,在函数的调用1a被初始化为3, b被初始化为5;在函数的调用2a被初始化为10, b被初始化为20

0x02 Pass By Value

当函数形参的传递方式为Pass By Value时,初始值被拷贝给变量,此时对形参的改动不会影响实参:

#include <iostream>

using std::cout;
using std:: endl;

void plus(int a) {
    a++;
}

int main() {
    int value = 10;
    plus(value);
    cout << value << endl;  // 10
    return 0;
}

上面的程序在plus函数中更改a的值不会影响value的值。

0x03 Pass By Reference

当函数形参的传递方式为Pass By Reference时 ,对形参的更改会影响实参:

#include <iostream>

using std::cout;
using std:: endl;

void plus(int &a) {
    a++;
}

int main() {
    int value = 10;
    plus(value);
    cout << value << endl;  // 11
    return 0;
}

使用引用可以避免拷贝,提升效率

拷贝很大的对象时性能可能会很低,甚至某些类型(包括IO类型在内)根本不支持拷贝操作,对于不支持拷贝或者性能很低时,函数只能通过Pass By Reference形式进行传递:

// 比较两个字符串
bool isLonger(const std::string& s1, const std::string& s2) {
    return s1.size() > s2.size();
}

上面的函数是为了比较两个string对象的长度,因为string的长度可能非常长(可能是一本书),所以拷贝过程可能狠低效,所以使用Pass By Reference避免拷贝动作的发生。

0x04 const形参(常量引用)

const和形参结合时,也有一些需要注意的点:

  • 形参的顶层const会被忽略,例如下面的函数会发生重定义报错;
void func(int a) {...}
void func(const int a) {...}

虽然C++支持函数重载,但是当使用func(5)调用时,编译器调用哪个函数都是可以的,会引发二义性,所以顶层const并不属于函数的重载条件。

  • 引用形参与const

形参的初始化方式与变量的初始化方式是一样的,我们可以使用非常量初始化一个底层const对象,但是反过来却不行,例如对于下面的函数:

bool isLess(const int& a, const int& b) {
    return a < b;
}

有两个常量ab;两个变量cd:

const int a = 1;
const int b = 2;
int c = 3;
int d = 4;

下面的函数调用都是合法的:

isLess(a, b);
isLess(c, d);

但是当函数定义为

bool isLess(int& a, int& b) {
    return a < b;
}

下面的调用是合法的

isLess(c, d);

但是下面的调用就会发生错误

isLess(a, b);

所以在设计函数时,尽量使用常量引用

0x05 类中的成员函数

假设我们设计一个复数(Complex)类,下面是类图:

在这里插入图片描述

下面是实现代码:

class Complex {
private:
    double re;
    double im;
public:
    Complex() {}
    Complex(double re, double im) : re(re), im(im) {}
    double getRe() const {
        return re;
    }
    void setRe(double re) {
        Complex::re = re;
    }
    double getIm() const {
        return im;
    }
    void setIm(double im) {
        Complex::im = im;
    }
};

上面代码的两个函数getRe()getIm()设定为const,这样做主要是由两个原因:

  • 第一个是因为这两个函数没有更改对象的属性,所以在函数的参数后面加上const关键字,但是要注意,这种写法只能应用于类中的成员函数,在外部的函数不能这样书写

  • 第二个也是为了避免常量对象调用这个函数,例如:

    声明一个复数对象,实部为3,虚部为2,因为不打算更改这个复数的实部和虚部,所以将其声明为常量对象,所以在一个常量对象调用上面的getIm()getRe()函数时,如果这两个函数不加const会引发错误,也就是说,这个类设计的并不理想。

    const Complex c(3, 2);
    // 如果类中的成员函数不声明为const,会导致错误
    c.getRe();
    c.getIm();
    

总结


  • Pass By Value 形参的改变不会影响实参
  • Pass By Reference形参的改变会影响实参,并且会避免拷贝的发生,提高效率
  • 在设计函数时,尽量使用常量引用
  • 类中的成员函数,不更改对象属性的函数建议定义为const
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值