<Effective C++>:Item 20:以pass-by-reference-to-const替代pass-by-value .

我们经常在函数传参的时候看到const &这样的形式,而不是简单的&或传值,这里面肯定是有大大的学问的

如果一个函数是pass-by-value,那么传入函数内部时,编译器会调用copy构造函数构造一份实参的副本,执行函数内部的逻辑,然后将这个副本返回,所以我们只是传值的话,是不会对原实参作改动的。
但是更严重的是,pass-by-value还有可能产生严重的性能浪费,以一个简单的继承体系为例:

class Person{
public:
    Person()=default;
    virtual ~Person()=default; //注意声明析构函数为virtual
    ...
private:
    string name;
    string number;
};
class Student:public Person{
public:
    Student()=default;
    ~Student()=default;
private:
    string stu_name;
    string stu_number;
};

接下来调用函数分析:

bool testFunc(Student item);
Student s;
testFunc(s);

传递参数时,Student调用一次copy构造函数,以及脱离作用域的一次析构调用,但是!Student继承自Person类,而且私有域内还有string类型的类成员,所以每次构造一个Student类,还会构造一个Person基类,加上私有域中string类也要调用构造函数…那么这么一个函数调用,其不可见部分的函数调用,可以说是非常影响性能的,最后一共要调用“六次构造函数和六次析构函数”
而如果我们使用pass-by-reference-to-const调用函数:

testFunc(const Student& item);

因为是&,所以传入实参时不会构造实参副本,也就免去了大量的构造-析构的开销,声明为const,说明实参是只读的,在函数内部不能对实参进行操作,在不改变语义的情况下,通过参数的传递方式,很自然的完成了成功的性能优化。
如果设计的基类中有virtual函数,那么pass-by-reference还避免了对象切割(slicing)问题,对象切割不是多态,多态是通过指针或者引用实现的,而对象切割是派生类直接向基类传递或者强制类型转换的时候,自己(基类)的部分在转换时丢失了,就像被切割了一样。

Person* item=new Person();
Student test;
...
item=&test;  //这是多态
*item=test;  //这是对象切割

所以如果我们在Person类中加入一个virtual函数,并且在Student中重写:

class Person{
...
    virtual void show(){
        cout<<name<<number<<endl;
    }
}

class Student{
...
    void show() override{
        cout<<stu_name<<stu_number<<endl;
    }
}

如果在某个函数中,我们需要调用其中的virtual函数,且需要避免对象切割问题,那么我们就应该使用pass-by-reference

void testSilce(Person item){
    item.show();
    ...
    }
void testSlice2(const Person& item){
    item.show();
    }
Student test;
testSlice(test);  //强制类型转换,发生对象切割
testSlice2(test);  //多态

在C++中,reference的底层实现其实还是靠指针的,所以如果函数传递的对象是built-in types [同时也包括了STL对象和函数对象!],那么其实传值也不是一个那么糟糕的方案,至少对于built-in types,传值是有性能优势的,毕竟不像我们上面举的例子,内置类型都是非常轻量化的类型,甚至效率要来的更加高。
但是我们并不能认为自定义类型的看似小型的类,也可以用pass-by-value的方法提高性能,这是不对的,copy构造函数的代价往往不是我们能够轻易准确估量的,甚至还和具体编译器的具体实现有关。
所以记住,除了built-in types , STL迭代器对象 , 函数对象,其他实践中,尽量使用pass-by-reference-to-const是一个良好的编程习惯!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值