条款20:宁以pass-by-reference-to-const替换pass-by-value
缺省情况下C++是以by value 传递的。其副本是对象本身的copy 构造函数自动调用的。
class Persion { private: std::string m_name; std::string m_addr; }; class Student : public Persion { private: std::string m_schoolName; std::string m_schoolAddr; }; bool IsValidStudent(Student s) {return true;}
调用
Student s1; bool b_s = IsValidStudent(s1);
IsValidStudent函数调用行为如下:编译器调用student拷贝构造函数拷贝实参到形参,然后形参使用完后再调用析构函数,但该类调用远不止这些,如student的父类也需要调用拷贝构造函数,而persion中有两个string成员也需要调用拷贝构造函数,还有student也有两个string成员需要调用拷贝构造函数,而且这些调用完后还需要调用析构函数。就因为一个pass-by-value操作,而需要花费这么多成本。
pass-by-reference-to-const
将上面的IsValidStudent函数修改如下
bool IsValidStudent(const Student &s) {return true;}
这样没有调用任何的构造函数和析构函数,形参实际是实参的副本,因此需要加上const保护实参免修改。
对象切割
当一个derived class对象以pass-by-value的形式传递给一个base-class对象时,base-class会调用拷贝构造函数,且会把derived class的所有行为都切割掉,即编译器只当成base-class处理。如果用reference和指针,可以防止对象切割。
class Persion { public: Persion(const string &name, const string& addr) : m_name(name), m_addr(addr) {} virtual ~Persion() {} //作为基类,需要定义虚析构函数 string GetName() const {return m_name;} string GetAddr() const {return m_addr;} virtual void DisPlay() {cout<<"Person\n";} private: std::string m_name; std::string m_addr; }; class Student : public Persion { public: Student(const Persion &persion, const string &sName, const string &sAddr) :Persion(persion.GetName(), persion.GetAddr()) ,m_schoolName(sName) ,m_schoolAddr(sAddr) { } string GetName() const {return m_schoolName;} virtual void DisPlay() {cout<<"Student\n";} private: std::string m_schoolName; std::string m_schoolAddr; }; void DisPlayPersion(Persion persion) { std::cout<<persion.GetName()<<endl; persion.DisPlay(); }
调用
Persion p("cuancuan", "guangzhou"); Student s1(p, "shiyan", "yuexiu"); DisPlayPersion(s1);
输出
cuancuan
Person
结果很明显,没有调用student的Display函数
修改函数接口参数为pass-by-reference-to-const
void DisPlayPersion(const Persion &persion)
同样调用结果如下
cuancuan
Student
看到结果,是不是有一种多态性的感觉,类似于将derived class地址赋给based clasa对象指针,调用虚函数DisPlay显示多态性。如果窥视c++编译器的底层,你会发现,references往往以指针实现出来,因此pass-by-reference通常意味着这真正传递的是指针。
因此如果你有个对象属于内置类型(如int),pass by reference或pass by reference to const时,选择pass by value并非没有道理。这个忠告也适用于STL的迭代器和函数对象,因为习惯上它们都被设计为pass by value。迭代器和函数对象的实践者有责任看看他们是否高效且不受切割问题的影响。
记住
①尽量以pass-by-reference-to-const替换pass-by-value.前者通常比较高效,并可避免切割问题(slicingproblem).
②以上规则并不适合与内置类型,以及STL的迭代器和函数对象.对它们而言,pass-by-value往往比较适合.
条款20:宁以pass-by-reference-to-const替换pass-by-value,布布扣,bubuko.com