使用pass-by-reference-to-const而不是pass-by-value
默认情况下,C++以传值(by-value)的方式传递对象至函数。
而C++也提供一种by-reference的方式。
那么,两者有什么去别呢?或者说,by-reference有什么好处吗?
性能问题
这些副本由对象的拷贝构造函数产出,这可能是pass-by-value费时的原因。
考虑一下代码。
class Person
{
private:
/* data */
string name;
string address;
public:
Person(/* args */)
{
cout << "Person ctor" << endl;
}
virtual ~Person() // 这里一定要有virtual
{
cout << "Person dtor" << endl;
}
};
class Student : Person
{
private:
/* data */
string schoolName;
string schoolAddress;
public:
Student(/* args */)
{
cout << "Student ctor" << endl;
}
~Student()
{
cout << "Student dtor" << endl;
}
};
bool validateStudent(Student s)
{
return true;
}
当前的validateStudent
函数会执行6次拷贝构造函数和6次析构函数,
分别是Person、Student和4个string。
而通过引用的方式没有任何构造函数和析构函数被调用。
bool validateStudent(const Student& s);
现在以pass-by-reference的方式进行调用时,将它声明为const是很有必要的,因为pass-by-value的时候调用者知道传入的是一份copy,不会对原来的Student对象产生任何改变,因为不这样做的话,传入reference会引起调用者的担心。
对象切割(Slicing)问题
现有代码
class Window
{
private:
/* data */
string _name;
public:
string name() const
{
return _name;
}
virtual void display() const
{
cout << "simple window" << endl;
}
Window(): _name("Window") {}
~Window() {}
};
class WindowWithScrollBar : public Window
{
private:
/* data */
public:
virtual void display() const
{
cout << "window with scrollbar" << endl;
}
WindowWithScrollBar() {}
~WindowWithScrollBar() {}
};
若传参方式是by-value
void printNameAndDisplay(Window w)
{
cout << w.name() << endl;
w.display();
}
那么将会产生对象切割问题,即使传入的是derived class对象,但是base class内的拷贝构造函数将会被调用,derived class内部的”特性“都会被切割掉。所以内部调用总是Windows::display()
。
总结
- 从C++编译器底层实现来看,reference往往是以指针实现,所以对内置类型而言,pass-by-value可能比pass-by-reference效率会高些。STL的迭代器和函数对象也适用此条。
- 内置类型都很小,于是所有小型的class都适用pass-by-value。这是个不靠谱的推论!
- 由于某些编译器对待内置类型和用户定义类型的态度截然不同。所以对于哪怕只有一个
double
成员的自定义类对象,也更应该以pass-by-reference的方式传递。