条款20:宁以pass-by-reference-to-const替换pass-by-value
在c语言中我们知道,函数传参时,直接传参会发生拷贝行为。因此,为了减少拷贝,我们通常会传引用。同样,这个规则在c++中也适用。同时c++中有类的概念,当我们传一个类类型的参数时,如果这个类较大,传值无疑会产生大量多余的拷贝行为,因此,可以通过传const引用的形式传参。
来看一个例子:
class Base { public: Base() { cout << "Base constructor" << endl; } ~Base() { cout << "Base destructor" << endl; } Base(const Base &) { cout << "Base copy destructor" << endl; } void output () const { cout << "Base output" << endl; } }; void output(Base base) { base.output(); } int main() { Base base; cout << "=============" << endl; output(base); return 0; }
当使用直接传值时输出:
Base constructor ============= Base copy destructor Base output Base destructor Base destructor
可以发现确实发生了一次拷贝行为。
而如果采用传引用的形式:
void output(const Base &base) { base.output(); }
程序输出:
Base constructor ============= Base output Base destructor
发现没有确实没有发生拷贝行为。
同时,传引用还可以避免对象切割问题。来看下面这个例子:
class Window { public: virtual void show() const; }; class WindowWithButton : public Window{ public: virtual void show() const override; }; void Window::show() const { cout << "window show" << endl; } void WindowWithButton::show() const { cout << "window with button show" << endl; } void func(Window w) { w.show(); } int main() { WindowWithButton windowWithButton; func(windowWithButton); return 0; }
WindowWithButton类继承自Window类。func函数接受一个Window类型的值,当我们传入一个WindowWithButton类型的实参时,期望的应该是调用WindowWithButton类的show方法,而程序输出:
window show
这正是因为参数是值传递的原因,当使用引用传递时:
void func(const Window &w) { w.show(); }
程序输出:
window with button show
引用书中一段话
如果窥视C++编译器的底层,你会发现,references往往以指针实现出来,因此pass by reference通常意味真正传递的是指针。因此如果你有个对象属于内置类型(例如int),pass by value往往比pass by reference的效率高些。对于内置类型而言,当你有机会选择采用pass-by-value或pass-by-reference-to-const时,选择pass-by-value并非没有道理。这个忠告也适用于STL的迭代器和函数对象,因为习惯上它们都被设计为passd by value。
条款21:必须返回对象时,别妄想返回其reference
当我们发现了值传递和引用传递的优劣时,恨不得什么情形下都使用引用,但有些情况下需要注意。函数中我们不能返回局部变量的引用或指针,因为当函数结束时,局部变量也会自动销毁,此时再返回它的引用或指针是未定义的行为,不要这样做。
同时,返回new出来的对象时,我们也没办法保证调用者会正确的析构该内存,因此会造成内存泄漏。
因此,有时为了保证正确性,我们不得不返回一个对象。
当你必须在“返回一个reference和返回一个object之间抉择时,你的工作就是挑出行为正确的那个。”