条款 22: 尽量用“传引用”而不用“传值”

        除非明确指定,函数的形参总是通过“实参的拷贝”来初始化的,函数的调用者得到的也是函数返回值的拷贝。用传值来传递对象,会调用大量的构造函数和析构函数,效率低下。所以,为避免这种潜在的昂贵的开销,就不要通过值来传递对象,而要通过引用

    const Student& returnStudent(const Student& s)
   { return s;}
       这会非常高效:没有构造函数或析构函数被调用,因为没有新的对象被创建。
        通过引用来传递参数还有另外一个优点: 它避免了所谓的 “切割问题 (slicingproblem)” 。 当一个派生类的对象作为基类对象被传递时,它(派生类对象)的作为派生类所具有的行为特性会被“切割”掉,从而变成了一个简单的基类对象。

      例如,假设设计这么一套实现图形窗口类:

class Window {
public:
string name() const; //  返回窗口名
virtual void display() const; //  绘制窗口内容
};
class WindowWithScrollBars: public Window {
public:
virtual void display() const;
};
//  一个受“切割问题”困扰的函数
void printNameAndDisplay(Window w)
{
cout << w.name();
w.display();
}
想象当用一个 WindowWithScrollBars 对象来调用这个函数时将发生什么:
WindowWithScrollBars wwsb;
printNameAndDisplay(wwsb);

数 w 将会作为一个 Windows 对象而被创建(它是通过值来传递的,记得吗?) ,所有 wwsb 所具有的作为WindowWithScrollBars 对象的行为特性都被“切割”掉了。printNameAndDisplay 内部,w 的行为就象是一个类 Window的对象(因为它本身就是一个 Window 的对象) ,而不管当初传到函数的对象类型是什么。尤其是, printNameAndDisplay 内部对 display 的调用总是Window::display ,而不是 WindowWithScrollBars::display 。


解决切割问题的方法是通过引用来传递 w:

//  一个不受“切割问题”困扰的函数
void printNameAndDisplay(const Window& w)
{
cout << w.name();
w.display();
}

现在 w 的行为就和传到函数的真实类型一致了。为了强调 w 虽然通过引用传递但在函数内部不能修改,就要采纳条款 21 的建议将它声明为 const。传递引用是个很好的做法,但它会导致自身的复杂性,最大的一个问题就是别名问题,这在条款 17 进行了讨论。另外,更重要的是,有时不能用引用来传递对象,参见条款 23。最后要说的是,引用几乎都是通过指针来实现的,所以通过引用传递对象实际上是传递指针。因此,如果是一个很小的对象——例如 int—— 传值实际上会比传引用更高效。


为什么使用引用可以解决切割问题:我的理解:

1.使用引用本质上是使用指针作为参数基类指针可以接受基类作为参数,也可以接受派生类作为参数,如果基类中含有虚函数,则使用指针可以实现多态性。




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值