侧边栏壁纸
博主头像
如此肤浅

但行好事,莫问前程!

  • 累计撰写 52 篇文章
  • 累计创建 6 个标签
  • 累计收到 6 条评论

目 录CONTENT

文章目录

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

如此肤浅
2022-05-28 / 0 评论 / 0 点赞 / 34 阅读 / 1,853 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-05-28,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

1. 尽量以 pass-by-reference-to-const 替换 pass-by-value。前者通常比较高效,并可避免切割问题(slicing problem) 。
2. 以上规则并不适用于内置类型,以及 STL 的迭代器和函数对象。对它们而言,pass-by-value 往往比较适当。

pass-by-value 的缺点

函数 pass-by-value 时,函数的参数是以实际参数的副本为初值,而调用端获得的同样是函数返回值的一个副本。这些副本由对象的 copy 构造函数产生,这个操作会使 pass-by-value 比较耗时。

class Person {
public:
	Person();
    virtual ~Person(); // 条款7解释了为什么是virtual
    ..
private:
	std::string name;
    std::string address;
};
class Student: public Person {
public:
	Student();
    ~Student();
    ...
private:
	std::string schoolName;
    std::string schoolAddress;
};
bool validateStudent(Student s); // 定义函数以 by value 方式接受参数

Student plato; // 创建一个学生对象
bool platoIsOK = validateStudent(plato); // 调用函数

在上述调用函数中,会以 plato 为蓝本调用 Student 的 copy 构造函数,将 s 初始化。同样,当 validateStudent 返回时,s 会被析构函数销毁。

此外,Student 对象内有两个 string 对象,因此每次构造 Student 对象时也就构造了两个 string 对象。同时, Student 对象继承自 Person 对象,所有每次构造 Student 时也构造了一个 Person 对象,由于一个 Person 包含两个 string 对象,因此又需要两个构造函数的调用。析构函数同理。

总之,以 by value 方式传递一个 Student 对象会导致6次构造和6次析构函数的调用!效率比较低!

pass-by-reference-to-const 的优点

  • 提高效率

    bool validateStudent(const Student& s);
    

    通过 pass-by-reference-to-const 能避免前面多次的构造函数和析构函数调用,效率高很多。

    并且,以 by value 方式接收一个 Student 参数,需要调用者知道这个对象受保护(不能对其进行修改)。而以 by reference 方式传递时,将它声明为 const 后,调用者则不能对它进行修改,无需再进行担忧。

  • 可避免对象切割问题
    当一个 derived class 对象以 by value 方式传递时,它会被视为一个 base class 对象,base class 的 copy 构造函数会被调用,而“造成此对象的行为像个 derived class 对象”的那些特化性质全被切割掉了,仅仅留下一个 base class 对象。

    假设你在一组类来实现一个图形窗口系统:

    class Window {
    public:
    	...
        std::string name() const; // 返回窗口的名称
        virtual viod display() const; // 显示窗口和其内容
    };
    
    class WindowWithScrollBars: public Window {
    public:
    	...
        virtual void display() const;
    };
    

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

    // 不正确!参数可能被切割
    void printNameAndDisplay(Window w)
    {
    	std::cout << w.name();
        w.display();
    }
    
    WindowWithScrollBars wwsb;
    printNameAndDisplay(wwsb);
    

    当调用 printNameAndDisplay 时,由于是 pass by value,参数 w 会被构造成一个 Window 对象,会使 wwsb 所特有的信息被切除。因此 printNameAndDisplay 内调用 display 调用的总是 Window::display。

    解决切割问题的办法是,以 by reference-to-const 的方式传递 w:

    void printNameAndDisplay(const Window& w)
    {
    	std::cout << w.name();
        w.display();
    }
    
0

评论区