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

xiaoxiao2021-02-28  61

我们经常在函数传参的时候看到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是一个良好的编程习惯!

转载请注明原文地址: https://www.6miu.com/read-36135.html

最新回复(0)