条款20(二):宁以pass-by-reference-to-const替换pass-by-value

127 篇文章 7 订阅
39 篇文章 3 订阅

条款20:宁以pass-by-reference-to-const替换pass-by-value

Prefer pass-by-reference-to-const to pass-by-value.

Slicing问题

by reference方式传递参数,也可以避免slicing(对象切割)问题
当一个derived class对象以by value方式进行传递,并被视为一个base class对象,base class的copy构造函数会被调用,然而:

  • “造成此对象的行为像一个derived class对象”的那些特质化的特征全部被切割掉了,仅仅留下了一个base class对象。

造成这种情况的原因是,这个对象正是base class构造函数进行建立的,然而这种现象是我们一定不希望看到的。举个例子,假设我们定义一组class,用来实现一个图形窗口系统:

class Window {
public:
    ...
    std::string name() const;       //返回窗口的名称
    virtual void display() const;   //显示窗口和其中的内容
};

class WindowWithScrollBars : public Window {
public:
    ...
    virtual void display() const;
};

对于所有的Window对象,都有一个名称,我们可以通过name函数获取。所有的窗口显示,我们也可以通过display函数来进行实现。
其中,display函数是一个virtual函数,这就意味着base class Window对象的显示方式和WindowWithScrollBars对象的显示方式是不同的。

而当我们希望写一个函数去打印窗口的名称,然后显示该窗口,下面的写法是错误的!

void printNameAndDisply(Window w)   //不正确,参数可能会被切割
{
    std::cout<<w.name();
    w.display();
}

当我们调用上述的参数并向其传递一个WindowWithScrollBars对象时:

WindowWithScrollBars wwsb;
printNameAndDisplay(wwsb);

此时,参数w会被构造称为一个Window对象,因为它是pass by value的。
于是,使得wwsb“之所以是一个WindowWithScrollBars对象”的所有特征都会被切割掉,简而言之:

  • 在printNameAndDisplay函数内不管是传递过来的对象时什么类型,参数w就像是一个Window对象。

因此,在printNameAndDisplay函数内调用display调用的总是Window::display,而绝不会是WindowWithScrollBars::display。

解决切割(slicing)问题的办法,就是以by reference-to-const方式传递w:

void printNameAndDisplay(const Window& w)
{
    std::cout<<w.name();
    w.display();
}

此时,传进来的窗口的是什么类型,w就表现出那种类型。

内置类型的情况

当我们观察C++编译器底层时,可以看到reference往往是以指针来实现的,因此:

  • pass by reference通常意味传递的是指针。

根据这点,如果我们需要传递的对象属于内置类型(例如int),pass by value往往比pass by reference的效率更高。
对于这一点,也同样适用于STL的迭代器和函数对象,因为习惯上,它们都被设计为passed by value。

因为内置类型都相当的小,所以可能有人就会认为,所有小型types都可以使用pass-by-value,甚至当它们是用户自定义的class也一样,这个结论是错误的!因为对象小不代表copy构造函数的代价就不高。
有许多对象——包括大多数STL容器——内含的东西只比一个指针多一些而已,但是复制这种对象却需要承担“赋值这些指针所指的每一样东西”。因此,代价也是非常昂贵的。

即时小型对象拥有并不昂贵的copy构造函数,在效率上也可能有差距。某些编译器在对待“内置类型”和“用户自定义类型”的态度上截然不同,即使两者拥有相同的底层表述(underlying representation)。

“小型的用户自定义类型不一定通过pass-by-value”的另一个理由是:

  • 作为一个用户自定义类型,其大小容易变化。

一个type目前虽然比较小,将来却可能会变得比较大,因为其内部实现可能会变化。

一般而言,我们可以认为:

  • pass-by-value代价不高的唯一对象就是内置类型和STL的迭代器和函数对象。

以至于所其他其他的任何东西,都应当尽量以pass-by-reference-to-const替换pass-by-value。

最后:

1,尽量以pass-by-reference-to-const替换pass-by-value。前者通常比较高效,并可以避免切割问题(slicing problem)。

2,以上的规则并不适用于内置类型,以及STL的迭代器和函数独享。对它们而言,pass-by-value往往比较适当。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值