effective C++ 3th 备忘3

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

会带来构造/析构的开销。此时形参是实参的副本。

  • passbyreference-to-const

没有任何构造函数或析构函数被调用,因为没有任何新对象被创建。

  • by reference方式传递参数也可以避免slicing(对象切割)问题。

当一个derived class对象以by value方式传递并被视为一个base class对象,base class的copy构造函数会被调用,而"造成此对象的行为像个derived class对象"的那些特化性质全被切割掉了,仅仅留下一个base class对象。

在printNameAndDisplay函数内不论传递过来的对象原本是什么类型,参数w就像一个Window对象(因为其类型是Window)。因此在printNameAndDisplay内调用display调用的总是Window::display,绝不会是WindowWithScrollBars::display。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class  Window
{
public :
     std::string name()  const ;        //返回窗口名称
     virtual  void  display()  const ;    //显示窗口和其内容
};
 
class  WindowWithScrollBars:  public  Window
{
public :
     virtual  void  display(}  const ;
};
 
void  printNameAndDisplay(Window w)   //不正确!参数可能被切割。
{
     std::cout << w.name();
     w.display();
}

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

1
2
3
4
5
void  printNameAndDisplay( const  Window &w)    //很好,参数不会被切割。
{
     std::cout << w.name();
     w.display();
}

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

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

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

12、条款21: 必须返回对象时,别妄想返回真reference

(译注:这里我补充说明:两次operator*调用的确各自改变了static Rational对象值,但由于它们返回的都是reference,因此调用端看到的永远是static Rational对象的"现值"。)

一个"必须返回新对象"的函数的正确写法是:就让那个函数返回一个新对象呗。

对Rational的operator*而言意味以下写法(或其他本质上等价的代码):

1
2
3
4
inlineconstRational operator* (constRational& lhs, constRational& rhs)
{
     return  Rational(lhs.n* rhs.n, lhs.d * rhs.d);
}


13、条款22: 将成员变量声明为private

14、条款43: 学习处理模板化墓类内的名称

正如注释所言,当base class被指定为MsgSender<CompanyZ> 时这段代码不合法,因为那个class并未提供sendClear函数!那就是为什么C++拒绝这个调用的原因:它知道base class templates有可能被特化,而那个特化版本可能不提供和一般性template相同的接口。因此它往往拒绝在templatized base classes(模板化基类,本例的MsgSender<Company>)内寻找继承而来的名称(本例的SendClear)。就某种意义而言,当我们从ObjectOrientedC++跨进TemplateC++(见条款1),继承就不像以前那般畅行无阻了。

为了重头来过,我们必须有某种办法令C++ "不进入templatized base classes观察" 的行为失效。有三个办法

  • 是在base class 函数调用动作之前加上"this->"

   this->sendClear(info);

  • 使用using声明式

    using MsgSender<Company>::sendClear;   //告诉编译器,请它假设sendClear位于baseclass内。

    void sendClearMsg(const Msglnfo& info)

    {

         sendClear(info);                                          //OK, 假设sendClear将被继承下来。

         }

  • 明白指出被调用的函数位于base class内

    void sendClearMsg(const Msglnfo& info)

    {

           MsgSender<Company>::sendClear(info);     //OK, 假设sendClear将被继承下来。

    }

但这往往是最不让人满意的→个解法,因为如果被调用的是virtual函数,上述的明确资格修饰(explicitqualification)会关闭"virtual绑定行为"。

不推荐使用第三种方式。


根本而言,本条款探讨的是,面对"指涉base class members"之无效references,编译器的诊断时间可能发生在早期(当解析derived class template的定义式时),也可能发生在晚期(当那些templates被特定之template实参具现化时)。C++的政策是宁愿较早诊断,这就是为什么"当base classes从templates中被具现化时"它假设它对那些base classes的内容毫无所悉的缘故。

  • 可在derived class templates内通过"this->" 指涉base class templates内的成员名称,或藉由一个明白写出的"base class资格修饰符"完成。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值