关闭

关于 pass-by-reference-to-const 和 pass-by-value

标签: 读书笔记c++博客
261人阅读 评论(0) 收藏 举报

第一次写博客,其实算一个读书笔记吧,我觉得记一下可能能增加我得印象,废话不说,直接正题。

这次记一个很小的问题, effective c++ 中 的条款20是:宁以pass-by-reference-to-const替换pass-by-value( 宁愿按const引用传递来替代按值传递),按值传递就是函数参数都是以实际实参的复件(副本)为初值,而调用端所获得的是函数返回值的一个复件,这些复件系由对象的copy构造函数产出,这可能使得pass-bu-value成为昂贵的(费时的)操作,下面是书中的源码:

<span style="font-size:18px;">class Person
{
 public:
  Person();
  ~virtual Person();
   ...
  private:
  std::string name;
  std::string address;
};
class Student: public Person {
public:
  Student();
  ~Student();
  ...
private:
  std::string schoolName;
  std::string schoolAddress;
};</span>

现在考虑如下代码:


bool validateStudent ( Student s ); 

Student plato;

bool platoIsOK = validateStudent(plato);

这显然是按值传递,它将会以plato为蓝本将s初始化,这将会调用Student的copy构造函数,当validateStudent返回时s将会被销毁(析构函数),所以按值传递的成本是“Student的一次拷贝函数的调用加上一次析构函数的调用”。


除此之外,Student对象还有两个string对象,所以每次构造一个Student对象就构造了两个string对象,此外! Student对象继承自Person对象,所以构造Student对象前又会构造一个Person对象, 而Person对象又有两个string对象。。。。,所以,总的计算下来,按值传递一个Student对象会导致”六次构造函数和六次析构函数的调用
!“(看起来很多的样子)。


然而,如果我们使用pass by reference-to-const (const引用): //前面讲过,用const修饰参数是一个很好的习惯,所以尽量使用const

bool validateStudent (const Student& s ) ;

这种传递方式效率会高很多,因为没有任何的构造函数或析构函数会被调用(没有任何新对象被创建),用const修饰是重要的,这将确保传入的Student不会被修改。


此外,按引用传递还有一个好处,就是能避免slicing(对象切割)问题,(什么鬼), 当一个子类class对象以by value方式传递并被视为一个父类对象,父类的copy构造函数会被调用,而”造成此对象行为像个子类对象“的哪些特化性质全被切割掉了,仅仅留下I一个父类对象”,现在看下面代码

<span style="font-size:18px;">class Window {
public:
  ...
  std::string name() const;  //返回窗口名称
  virtual void display() const; //显示窗口及其内容
}

class WindowWithScrollBare: public window {
 public:
   ...
   virtual void display() const;
};</span>


假设你希望写个函数打印窗口名称,然后显示该窗口,下面是错误的示范

<span style="font-size:18px;">void printNameAndDisplay( Window w )   //不正确,参数可能被切割
{
  std::cout<<w.name();
  w.display();
}</span>

当我们给它传递一个WindowWithScrollBare对象时,

WindowWithScrollBare wwsb;

printNameAndDisplay(wwsb);

参数w会被构造成Window对象而不是WindowWithScrollBare对象,而wwsb的所有特化信息会被全部切除,在printNameAndDisplay函数内不论传递过来的对象是什么类型,

参数w就像一个window对象(因为他的类型是window)。 因此在printNameAndDisplay内调用display调用的总是Window::display,绝不会是WindowWithScrollBare.Display.


解决切割(slicing)的办法就是pass by reference-to-const

<span style="font-size:18px;">void printNameAndDisplay( const Window& w )   //不正确,参数可能被切割
{
  std::cout<<w.name();
  w.display();
}</span>

这样,传进来的窗口是什么类型,w就表现出那种类型



请记住:

1.尽量以pass-by-reference-to-const替换pass-by-value。前者通常比较高效,并且可避免切割问题。

2.以上规则并不适用于内置类型,以及STL的迭代器和函数对象。对它们而言,pass-by-value往往比较合适。



0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:50016次
    • 积分:1496
    • 等级:
    • 排名:千里之外
    • 原创:99篇
    • 转载:47篇
    • 译文:0篇
    • 评论:4条
    最新评论