在C++的世界里,语句块是封装的一种表现形式,而语句块大都以function的语法格式呈现。在使用function时,如何使得function的性能最优?可以从入参和返回值方面考量,这点是C++的语法规则,具有普适性;也可以从函数的执行语句块方面考量,这是算法实现问题,不同场景,不同应用,内部实现不同,实现最优,当有千人千面,很难有统一的规则。为此,此文只探讨从入参和返回值方面如何提高函数效率。
一、参数(parameter list)输入
函数入参的方式有两种:传值(pass by value)和传引用(pass by reference)。
所谓传值,就是对要传入函数的对象进行copy,然后将copy的对象传入函数。而对象的copy,就会涉及对象的创建(构造函数调用)和对原对象内容的赋值(copy构造或者赋值运算)。特别是自定义的数据类型,由于其属性(field)的嵌套深度,会导致一连窜的对象属性的创建与copy,给函数调用带来费时的操作。举例如下:
struct People
{People(...) {...}...
private:string name;int age;bool sex;
};struct Address
{Address(...) {...}...
private:string name;string location;
};struct Home
{Home(...) { ... }...
private:People people;string name;Address address;
};Home* copyHome(Home home)
{...
}
调用上述copyHome函数,就会对home对象进行copy,而Home类中有属性People、string、Address,这些属性也会进行创建和copy,极大的降低了函数的调用效率。还有C++中大量使用面向接口编程,设计类的继承,如果将子类赋值给父类,然后把父类传入函数,将导致子类对父类的扩展特性丢弃掉,即所谓的对象切割问题(slicing)。因此,对于自定义的数据类型,使用pass by value是一个坏的选择。
所谓传引用(pass by reference),就是给要传入的对象取个别名,将这个别名传入函数体。传引用就不存在对象的创建与copy,并且在继承体系也不存在对象切割问题。因此,对于自定义的数据类型,使用传引用是优质代码的选择。如果不想改变传入对象的值,最好使用常值引用(pass by reference to const)。举例如下:
Home* copyHome(const Home& home)
{...
}
【特例】如果是内置数据类型、STL中的迭代器、函数对象,上述讨论不适合,当采用pass by value形式。
二、函数返回值(return value)
对于要返回函数体内的局部对象(local object),最好是返回该对象的copy,不要返回其reference。虽然可以将局部对象static化,但在多任务环境下带来重大问题。