文章目录
pass-by-value的问题——116
默认情况下C++以by value方式传递对象至函数。但是会调用copy构造函数,产生实参的复件,这可能使得pass-by-value成为昂贵的操作。更具体点,pass-by-value的参数的传递成本使“一次copy构造函数的调用,加上一次析构函数的调用”。我们来分析下列程序:
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;
};
bool validateStudent(Student s);
Student plato;
bool platoIsOK=validateStudent(plato);
上面函数被调用时,因为Student对象内有两个string对象所以每次构造一个Student对象也就构造了两个string对象。此外Student对象继承自Person对象,所以每次构造Student对象也必须构造出一个Person对象。一个Person对象又有两个string对象,一次每一次Person构造动作又需承担两个string构造动作。最终,以by value方式传递一个Student对象会导致调用六次构造函数和六次析构函数!
采用pass by reference-to-const方法——117
我们将函数声明为:
bool validateStudent(const Student& s); //参数声明为const让调用者不能改变传入的Student
这种传递方式的效率高得多:没有任何构造函数或析构函数被调用,因为没有任何新对象被创建。这里必须提一下:引用不是一个对象(C++ primer 5th)。
以by reference方式传递参数也可以避免slicing(对象切割)问题——118
这里主要将如果以by value方式传递参数,当一个派生类对象如果被视为一个基类对象,基类的copy构造函数会被调用,仅仅留下一个基类对象。比如定义一个图形窗口系统:
class Window{
public:
...
std::string name() const;
virtual void display() const;
};
class WindowWithScrollBars:public Window{
public:
..
virtual void dispaly() const;
};
我们再写一个打印窗口名称:
void printNameAndDisplay(Window w){
std::cout<<w.name();
w.display();
}
调用上面函数:
WindowWithScrollBars wwsb;
printNameAndDisplay(wwsb);
上面的调用给你实参wwsb表现的就是一个基类特征,而不是派生类特征,因为形参w就是基类类型。这就是切割问题。
解决切割问题的办法,就是以by reference-to-const的方式传递w:
void printNameAndDisplay(const Window& w)
{
std::cout<<w.name();
w.display();
}
内置类型不适合采用常量引用传递——119
C++编译器的底层中,references往往以指针实现出来,因此引用传递通常意味真正传递的是指针。因此如果你有个对象属于内置类型(例如int),pass by value往往比pass by reference的效率高些。这个对STL的迭代器和函数对象也适用。
一般而言,可以合理假设“pass-by-value并不昂贵”的唯一对象就是内置类型和STL的迭代器和函数对象。
总结——120
(1)尽量以pass-by-reference-to-const替换pass-by-value。前者通常比较高效,并可避免切割问题。
(2)以上规则并不适用于内置类型,以及STL的迭代器和函数对象。对它们而言,pass-by-value往往比较适当。