大学IT网 - 最懂大学生的IT学习网站! QQ资料交流群:367606806
当前位置:大学IT网 > C++技巧 > C++将强制转型减到最少

C++将强制转型减到最少(2)

关键词:C++强制转型  阅读(1056) 赞(12)

[摘要]本文主要是对C++将强制转型减到最少的讲解,希望对您学习C++有所帮助!

 

我突出了代码中的强迫转型。(这是一个新作风的强迫转型,但是运用旧作风的强迫转型也于事无补。)正像你所希冀的,代码将 *this 强迫转型为一个 Window。因此调用 onResize 的结果就是调用 Window::onResize。你也许并不等候它没有调用当前对象的那个函数!作为替代,强迫转型创建了一个 *this 的基类部分的新的,暂时的拷贝,然后调用这个拷贝的 onResize!上面的代码没有调用当前对象的 Window::onResize,然后再对这个对象执行 SpecialWindow 特有的举措——它在对当前对象执行 SpecialWindow 特有的举措之前,调用了当前对象的基类部分的一份拷贝的 Window::onResize。假定 Window::onResize 改动了当前对象(可以性并不小,由于 onResize 是一个 non-const 成员函数),当前对象并不会改动。作为替代,那个对象的一份拷贝被改动。假定 SpecialWindow::onResize 改动了当前对象,无论如何,当前对象将被改动,招致的境况是那些代码使当前对象进入一种病态,没有做基类的变卦,却做了派生类的变卦。

处置方法就是消弭强迫转型,用你真正想表达的来替代它。你不应该哄骗编译器将 *this 当作一个基类对象来处置,你应该调用当前对象的 onResize 的基类版本。就是这样:   

class SpecialWindow: public Window {

public:

virtual void onResize() {

Window::onResize(); // call Window::onResize

... // on *this

}

...   

};

这个例子也标明假定你发现自己要做强迫转型,这就是你可以做错了某事的一个信号。在你想用 dynamic_cast 时尤其如此。

在探求 dynamic_cast 的设计意图之前,值得留意的是很多 dynamic_cast 的完成都相当慢。例如,至少有一种通用的完成部分地基于对类名字中止字符串比较。假定你在一个位于四层深的单承袭体系中的对象上执行 dynamic_cast,在这样一个完成下的每一个 dynamic_cast 都要付出相当于四次调用 strcmp 来比较类名字的本钱。关于一个更深的或运用了多承袭的承袭体系,付出的代价会愈加昂贵。一些完成用这种方法义务是有缘由的(它们不得不这样做以支持静态链接)。虽然如此,除了在普遍意义上警惕强迫转型外,在功用敏感的代码中,你应该特别警惕 dynamic_casts。

对 dynamic_cast 的需求通常发作在这种情况下:你要在一个你确信为派生类的对象上执行派生类的操作,但是你只能经过一个基类的指针或引用来操控这个对象。有两个普通的方法可以避免这个效果。

  第一个,运用存储着直接指向派生类对象的指针的容器,从而消弭经过基类接口操控这个对象的需求。例如,假定在我们的 Window/SpecialWindow 承袭体系中,只需 SpecialWindows 支持 blinking,关于这样的做法:   

class Window { ... };   

class SpecialWindow: public Window {

public:

void blink();

...

};

typedef // see Item 13 for info

std::vector > 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(iter->get()))

psw->blink();

}

设法用如下方法替代:   

typedef std::vector > VPSW;   

VPSW winPtrs;   

...   

for (VPSW::iterator iter = winPtrs.begin(); // better code: uses

iter != winPtrs.end(); // no dynamic_cast

++iter)

(*iter)->blink();

当然,这个方法不允许你在同一个容器中存储一切可以的 Window 的派生类的指针。为了与不同的窗口类型一同义务,你可以需求多个类型安全(type-safe)的容器。

一个候选方法可以让你经过一个基类的接口操控一切可以的 Window 派生类,就是在基类中提供一个让你做你想做的事情的虚函数。例如,虽然只需 SpecialWindows 能 blink,在基类中声明这个函数,并提供一个什么都不做的缺省完成或许是有意义的:   

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 > 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

无论哪种方法——运用类型安全的容器或在承袭体系中上移虚函数——都不是四处适用的,但在很多情况下,它们提供了 dynamic_casting 之外另一个可行的候选方法。当它们可用时,你应该加以运用。

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

class Window { ... };   

... // derived classes are defined here   

typedef std::vector > VPW;   

VPW winPtrs;   

...   

for (VPW::iterator iter = winPtrs.begin(); iter != winPtrs.end(); ++iter)

{

 if (SpecialWindow1 *psw1 = dynamic_cast(iter->get())) { ... }   

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

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

...

}

这样的 C++ 会生成的代码又大又慢,而且很脆弱,由于每次 Window 类承袭体系发作变化,一切这样的代码都要必需被反省,以确认能否需求更新。(例如,假定添加了一个新的派生类,在上面的极联中或许就需求参与一个新的条件分支。)看起来类似这样的代码应该总是用基于虚函数的调用的某种东西来交流。 好的 C++ 极少运用强迫转型,但在通常情况下完全去除也不实际。例如,从 int 到 double 的强迫转型,就是对强迫转型的合理运用,虽然它并不是绝对必要。(那些代码应该被重写,声明一个新的类型为 double 的变量,并用 x 的值中止初始化。)就像大多数可疑的结构成分,强迫转型应该被尽可以地隔离,典型情况是隐藏在函数内部,用函数的接口维护调用者远离内部的污秽的义务。

Things to Remember

·避免强迫转型的随时运用,特别是在功用敏感的代码中运用 dynamic_casts,假定一个设计需求强迫转型,设法开发一个没有强迫转型的侯选方案。

·假定必需求强迫转型,设法将它隐藏在一个函数中。客户可以用调用那个函数来替代在他们自己的代码中参与强迫转型。

·尽量用 C++ 作风的强迫转型交流旧作风的强迫转型。它们更容易被留意到,而且他们做的事情也愈加明白。

«上一页12下一页»


相关评论