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(); }
评论区