[翻译] Effective C++, 3rd Edition, Item 27: 最少化 casting(强制转型)(下)

(点击此处,接上篇)

在深入探究 dynamic_cast 的设计意图之前,值得留意的是多数 dynamic_cast 的实现都相当慢。例如,至少有一种通用的实现部分地基于对 class names(类名)进行 string comparisons(字符串比较)。如果你在位于一个 single-inheritance hierarchy(单继承体系)四层深处的一个 object 上执行 dynamic_cast,在这样一个实现下的每一个 dynamic_cast 都要付出相当于四次调用 strcmp 来比较 class names(类名)的成本。对于一个更深的或使用了 multiple inheritance(多继承)的 hierarchy(继承体系),付出的代价会更加昂贵。一些实现用这种方法工作是有原因的(它们不得不这样做以支持 dynamic linking(动态链接))。尽管如此,除了在普遍意义上警惕 casts(强制转型)外,在性能敏感的代码中,你应该特别警惕 dynamic_casts。

dynamic_cast 的需要通常发生在这种情况下:你要在一个你确信为 derived class object(派生类对象)上执行 derived class operations(派生类操作),但是你只能通过一个 pointer- or reference-to-base(基类指针或引用)来操控这个对象。有两个普通方法可以避免这个问题。

第一个,直接使用存储着指向 derived class objects(派生类对象)的指针(通常是智能指针——参见 Item 13)的 containers(容器),从而消除通过 base class interfaces(基类接口)操控这个 objects 的需要。例如,如果在我们的 Window/SpecialWindow 继承体系中,只有 SpecialWindows 支持 blinking,对于这样的做法:

class Window { ... };

class SpecialWindow: public Window {
public:
  void blink();
  ...
};
typedef                                            // see Item 13 for info
  std::vector<std::tr1::shared_ptr<Window> > VPW;  // on tr1::shared_ptr

VPW winPtrs;

...

for (VPW::iterator iter = winPtrs.begin();         // undesirable code:
     iter != winPtrs.end();                        // uses dynamic_cast
     ++iter) {
  if (SpecialWindow *psw = dynamic_cast<SpecialWindow*>(iter->get()))
     psw->blink();
}

设法用如下方法代替:

typedef std::vector<std::tr1::shared_ptr<SpecialWindow> > VPSW;

VPSW winPtrs;

...

for (VPSW::iterator iter = winPtrs.begin();        // better code: uses
     iter != winPtrs.end();                        // no dynamic_cast
     ++iter)
  (*iter)->blink();

当然,这个方法不允许你在同一个容器中存储指向全部可能存在的 Window 的 derivatives(派生物)的指针。为了与不同的窗口类型一起工作,你可能需要多个 type-safe containers(类型安全容器)。

一个可以让你通过一个 base class interface(基类接口)操控全部可能存在的 Window 的 derivatives(派生物)的可选方法,就是在这个 base class(基类)中提供让你做你要做的事情的 virtual functions(虚函数)。例如,尽管只有 SpecialWindows 能 blink,在 base class(基类)中声明这个函数,并提供一个什么都不做的缺省实现或许是有意义的:

class Window {
public:
  virtual void blink() {}                       // default impl is no-op;
  ...                                           // see Item 34 for why
};                                              // a default impl may be
                                                // a bad idea

class SpecialWindow: public Window {
public:
  virtual void blink() { ... }                  // in this class, blink
  ...                                           // does something
};

typedef std::vector<std::tr1::shared_ptr<Window> > VPW;

VPW winPtrs;                                    // container holds
                                                // (ptrs to) all possible
...                                             // Window types

for (VPW::iterator iter = winPtrs.begin();
     iter != winPtrs.end();
     ++iter)                                    // note lack of
  (*iter)->blink();                             // dynamic_cast

无论哪种方法——使用 type-safe containers(类型安全容器)或在 hierarchy(继承体系)中上移 virtual functions(虚函数)——都不是到处适用的,但在很多情况下,它们提供了 dynamic_casting 之外的可行的候选方法。当它们可用时,你应该加以利用。

你应该绝对避免的一件东西就是包含了级联 dynamic_casts 的设计,也就是说,看起来类似这样的任何东西:

class Window { ... };

...                                     // derived classes are defined here

typedef std::vector<std::tr1::shared_ptr<Window> > VPW;

VPW winPtrs;

...

for (VPW::iterator iter = winPtrs.begin(); iter != winPtrs.end(); ++iter)
{
  if (SpecialWindow1 *psw1 =
       dynamic_cast<SpecialWindow1*>(iter->get())) { ... }

  else if (SpecialWindow2 *psw2 =
            dynamic_cast<SpecialWindow2*>(iter->get())) { ... }

  else if (SpecialWindow3 *psw3 =
            dynamic_cast<SpecialWindow3*>(iter->get())) { ... }

  ...
}

这样的 C++ 会生成又大又慢的代码,而且很脆弱,因为每次 Window class hierarchy(类继承体系)发生变化,所有这样的代码都要必须被检查,以确认是否需要更新。(例如,如果增加了一个新的 derived class(派生类),在上面的级联中或许就需要加入一个新的条件分支。)看起来类似这样的代码应该总是用基于 virtual function calls(虚函数调用)的某种东西来替换。

好的 C++ 极少使用 casts(强制转型),但在通常情况下完全去除也不实际。例如,第 118 页从 intdouble 的 cast(强制转型),就是对 cast(强制转型)的合理运用,虽然它并不是绝对必要。(那些代码可以被重写为,声明一个新的 double 类型的变量,并用 x 的值进行初始化。)就像最可疑的结构成分,casts(强制转型)应该被尽可能地隔离,典型情况是隐藏在函数内部,用函数的接口保护调用者远离内部的污秽的工作。

Things to Remember

  • 避免 casts(强制转型)的随时应用,特别是在性能敏感的代码中应用 dynamic_casts,如果一个设计需要 casting(强制转型),设法开发一个 cast-free(没有强制转型)的侯选方案。
  • 如果必须要 casting(强制转型),设法将它隐藏在一个函数中。客户就可以用调用那个函数来代替在他们自己的代码中加入 casts(强制转型)。
  • 尽量用 C++-style casts(C++ 风格强制转型)替换 old-style casts(旧风格强制转型)。它们更容易被注意到,而且他们做的事情也更加明确。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值