条款20: 宁以pass-by-reference-to-const 替换 pass-by-value
发布日期:2021-05-07 03:15:35 浏览次数:19 分类:精选文章

本文共 2043 字,大约阅读时间需要 6 分钟。

1、为什么要宁以pass-by-reference-to-const 替换 pass-by-value

效率方面

缺省情况下,C++以by value 方式传递对象至(或来自)函数。

除非你另外指定,否则函数参数都是以实际实参的副本为初值,而调用段所获得的亦是函数返回值的一个副本。这些副本都是由对象的copy构造函数产出的,这可能使得pass-by-value 称为昂贵的(费时的)操作。

考虑下述例子:

class Person {   public:    Person();    virtual ~Person();    ...private:    string name;    string address;};class Student: public Person {   public:    Student();    ~Student();    ...private:    string schoolName;    string schoolAddress;    };bool validateStudent(Student s);Student plato;//传递参数方式一bool platoIsOK = validateStudent(plato);//传递参数方式二bool validateStudent(const Student& s);

以方式一传递参数时,由于C++默认的是by value 的方式,因此,会构造一个Student对象,并且以plato进行初始化。这会导致调用Student的构造函数,而当函数返回时,势必又要调用其析构函数。 这只是表面,由于Student继承自Person,因此在创建Student时,势必要调用其基类的构造函数来初始化其基类部分,销毁时也是同样的。而又由于Person 和Student类中又内涵string对象,因此在构建时,又要调用string对象的构造和析构函数。全部算在一起,一共会调用6次析构函数和6次构造函数。这是非常耗时的操作。

而避免上述耗时的方法也非常简单,即以pass-by-value-to-const方式传递参数即可。这样的传递方式不会有任何新的对象被创建,因此也不会调用析构函数和构造函数。

2、第二个原因,避免slicing(对象切割)问题

(1)什么是对象切割问题?

一个函数的形参,接收的是一个基类的对象。 但如果调用这个函数时,传递的是一个派生类对象的话,那么形参只会构造这个派生类对象的基类部分作为实参。因此,当在函数内使用这个派生类对象时,它的所有被特化的部分都会表现的是基类的特性。

举栗子:

class Window {   public:    ...    string name() const;    virtual void display() const;};class WindowWithScrollBars: public Window {   public:    ...    virtual void display() const;};void printNameAndDisplay(Window w) //造成对象切割问题{       cout << w.name();    w.display();}//解决办法void printNameAndDisplay(const Window& w){       cout << w.name();    w.display();}
(2)解决的方法

参数以pass-by-reference-to-const 方式传递。

3、pass-by-reference-to-const和pass-by-value的区别,及适用场景?

pass-by-reference-to-const 的底层实现是指针,pass-by-reference-to-const方式通常意味着传递的是指针。因此,如果是内置类型的参数,pass-by-value比pass-by-reference-to-const的效率更高一些。同样的,对于STL的迭代器,以及函数对象,pass-by-value比pass-by-reference-to-const的效率更高一些。

因此对于内置类型、STL迭代器、函数对象 这三类,使用by value方式传递参数更好。

4、是不是小型的type都可以pass-by-value?

不是的,从下述三个方面考虑。

(1)小型的type 可能在今后的维护中变大。
(2)小型的type 的copy构造函数可能代价依然很大。
(3)即使小型的type 的copy构造函数的代价不大,编译器对待自定义类型和内置类型的态度是截然不同的。例如:编译器往往拒绝把对象放进缓存器,而接受把内置类型放进缓存器,即使它们的底层描述是一样的。能放进缓存器,效率肯定就会高一点。
上一篇:条款21:必须返回对象时,别妄想返回其reference
下一篇:条款19:设计class犹如设计type

发表评论

最新留言

表示我来过!
[***.240.166.169]2025年03月24日 14时45分34秒