我们用pass-by-reference-to-const替换pass-by-value主要是有两个方面的考虑:
1) 效率问题
缺省情况下C++以by value方式传递对象。除非你另外指定,否则函数参数都是以实际实参的复件(副本)为初值,其返回值也是一个复件。这些复件由对象的copy构造函数产出。因此pass-by-value是一个费时操作。例如:
class Person {};
class Student : pbulic Person { }; //学生类
现在有一个函数:
bool validateStudent(Student s); //函数以by value方式接受学生
student plato;
bool platoIsOk = validateStudent(plato); //调用函数
上面的传值会全Student的构造函数会被调用,以plato初始化s,同时在函数结束时,s会被销毁。不仅如此Student类中的所有数量成员的copy构造函数以及基类中的所有成员的构造函数也都会被调用,在销毁时,针对每一个构造函数还要调用相应的析构函数。这造成了巨大的开销。所以我们可以使用pass-by-reference-to-const替换pass-by-value。如下:
bool validateStudent(const Student& s); //函数以by-reference-to-const方式接受学生
这种传递效率高多了,没有任何的构造函数和析构函数被调用,因为没有任何的对象被创建。注意这里使用了const,在by value时如果修改发生,修改的是副本,原来数据没有变化。为了使by-reference时原来数据不会被修改,必须使用const来修饰。
2) by value可能造成对象分割(slicing)
当一个derived class对象以by value方式传递并被视为一个base class对象,base class的copy构造函数会被调用,这只会构造一个base class对象。而derived class中独有的特性全部被丢掉了。例如:
class Window {
pbulic:
....
string name() const; //返回窗口名称
virtual void display() const; //显示窗口和其内容 是个virtual函数
};
class WindowWithScrollBars: pbulic Window {
public:
....
virtual void display() const;
};
现在有一个函数:
void printNameAndDisplay(Window w) //参数可能被分割
{
w.display();
}
当你调用上面的函数,给它一个WindowWithScrollBars对象时,如下:
WindowWithScrollBars wwsb;
printNameAndDisplay(wwsb);
由于是passed by value,所以参数会被构造成一个Window对象,不管你传给它是什么类型的对象。所以在函数调用display函数调用的总是Window::display,绝不会是WindowWithScrollBars::display。解决的方法就是以pass-by-reference-to-const的方式传递w。如下:
void printNameAndDisplay(const Window& w) //参数不会被分割
{
w.display();
}
实际这就实现了一个多态了。
3) C++编译器对内置类型和自定义类型的不同处理
一般而言,你可以合理的假设对内置类型和STL的迭代器和函数对象使用pass-by-value是好的,而对其它的自定义的类型最好是使用pass-by-reference-to-const。
总结:
1) 尽量以pass-by-reference-to-const替换pass-by-value,前者通常比较高效,并可避免切割(slicing problem)
2) 以上规则并不适用内置类型,以及STL的迭代器和函数对象,对它们而言pass-by-value往往比较适当
Effective C++读书笔记之宁以pass-by-reference-to-const替换pass-by-value
最新推荐文章于 2024-03-09 15:59:05 发布