您的位置:首页 > 产品设计 > UI/UE

条款20:宁以pass-by-reference-to-const替换pass-by-value

2009-10-30 13:59 525 查看
   我们考虑以下class继承体系:
 class Person
 {
 public:
  Person();
  virtual ~Person();
  ...
 private:
  string name;
  string address;
 };

 class Student : public Person
 {
 public:
  Student();
  ~Student();
  ...
 private:
  string schoolName;
  string schoolAddress;
 };
现在考虑以下代码,其中调用函数validateStudent,需要一个Student实参(by value)并返回它是否有效;
bool validateStudent(Student s); //函数以by value方式接受参数

Student plato;
bool platoIsOk = validateStudent(plato);
对此函数而言,参数的传递成本是“一次Student copy构造函数调用,加上一次Student析构函数调用”.
我们来具体看一下对于此函数调用到底做了哪些操作:
Student对象内有两个string对象,所以每次构造一个Student对象也就构造了两个string对象。此外Student对象继承自Person对象,所以每次构造Student对象也必须构造一个Person对象。一个Person对象又有两个string对象在其中,因此每一次Person构造动作又需承担两个string构造工作。最终结果是:调用了一次Student copy构造函数、一次Person copy构造函数、四次string copy构造函数。当函数内的那个Student复件被销毁,每一个构造函数调用动作都需要一个对应的析构函数调用动作。因此,以by value 方式传递一个Student对象,总体成本是“六次构造函数和六次析构函数”。
这样传递的成本是相当高的,如果做下面的修改,就可以避免那些构造和析构的动作
bool validateStudent(Student &s); //函数以by-reference方式接受参数

Student plato;
bool platoIsOk = validateStudent(plato);
如果我们并不需要对传入的Student作任何改变,我们可以对上面的版本进行修订
bool validateStudent(const Student &s); //函数以by-reference-to-const方式接受参数

Student plato;
bool platoIsOk = validateStudent(plato);

另外,以by reference方式传递参数也可以避免slicing(对象切割)问题,我们来看看下面这个例子:
class Window
{
public:
 ...
 string name() const;
 virtual void display() const;
};

class WindowWithScrollBars : public Window
{
public:
 ...
 void display() const;
};
所有的Window对象都有一个名称,你可以通过name函数取得它。所有的窗口都可以显示,你可以通过display函数完成它。display是个virtual函数,这意味简单朴素的base class Window对象的显示方式和华丽高贵的WindowWithScrollBars对象的显示方式不同。
现在我们写这样一个函数打印窗口名称,然后显示该窗口。
void printNameAndDisplay(Window w)  //by value
{
 cout << w.name() << endl;
 w.display();
}
我们现在传递一个WindowWithScrollBars对象给该函数:
WindowWithScrollBars wwsb;
printNameAndDisplay( wwsb );
由于是passed-by-value,在printNameAndDisplay函数内无论传递过来的对象原本是什么类型,参数w就像一个Window对象。因此在printNameAndDisplay内调用display调用的总是Window::display,绝不会是WindowWithScrollBars::display。
解决切割问题的办法,就是以by reference-to-const的方式传递w;
void printNameAndDisplay(const Window &w)  //by reference
{
 cout << w.name() << endl;
 w.display();
}
如果窥视C++编译器的底层,reference往往以指针实现出来,因此pass by reference通常意味着真正传递的是指针。
对于内置类型,pass-by-value往往比pass by reference的效率高些。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐