第一次写博客,其实算一个读书笔记吧,我觉得记一下可能能增加我得印象,废话不说,直接正题。
这次记一个很小的问题, 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往往比较合适。