码迷,mamicode.com
首页 > 编程语言 > 详细

Effective C++:条款27:尽量少做转型动作

时间:2014-06-24 20:57:53      阅读:253      评论:0      收藏:0      [点我收藏+]

标签:style   class   blog   code   ext   color   

(一)C风格旧式转型:

(T)expression

T(expression)


(二)C++提供四种新式转型:

(1)const_cast<T>(expression):去除表达式的常量性,是C++中唯一能做此操作的转型操作符。

(2)dynamic_cast<T>(expression):主要用来执行“安全向下转型”,即用来决定某对象是否归属继承体系中的某个类型。它是唯一无法由旧式语法执行的动作,也是唯一可能耗费重大运行成本的转型动作。

(3)reinterpret_cast<T>(expression):试图执行低级转型(例如将一个指向整数变量的指针转换为整数),实际动作及结果可能与编译器相关,因而降低了可移植性。

(4)static_cast<T>(expression):隐式转换。注意虽然可以使非const类型转换为const,但反之不能(这个只能由const_cast来完成)。


(三)新式转型比较受欢迎,原因:

1,很容易在代码中被辨别出来;

2,各转型动作的目标愈窄化,编译器愈可能诊断出错误的运用。


(四)唯一使用旧式转型的时机是当调用一个explicit构造函数将一个对象传递给一个函数时:

class Widget{ 
public: 
    explicit Widget(int size); 
    ... 
}; 
void doSomeWork(const Widget &w); 
doSomeWork(Widget(15));//函数风格的转型动作创建一个Widget 
doSomeWork(static_cast<Widget>(15));//c++风格的转型动作创建一个Widget对象


(五)先看下面这个代码:

class Base{...}; 
class Derived : public Base{...}; 
Derived d; 
Base* pb = &d;//隐喻的将derived*转换成Base*
在这里表明:单一的对象可能拥有一个以上的地址。这里我们只是建立一个base class指针指向一个derived class对象,但有时候上述的两个指针值并不相同。这种情况下会有个偏移量offset在运行期被施行于Derived*指针身上,用于取得正确的Base*指针值。

实际上一旦使用多重继承,这事几乎一直发生.即使在单一继承中也可能发生.恐怖!为什么会发生这样的事情呢?因为对象的布局方式和它们的地址计算发式随着编译器的不同而不同,这就以为着写出"根据对象如何布局"而写出的转型代码在某一平台上行得通,在其它平台上则不一定。


(六)
另一件关于转型的有趣事情是:我们很容易写出某些似是而非的代码(其他语言中也许是对的)。例如SpecialWindow的onResize被要求首先调用Window的onResize。下面是看起来对,实际上错:

class Window { 
public: 
    virtual void onResize() {...} 
}; 
class SpecialWinddow : public Window { 
public: 
    virtual void onResize() { 
       static_cast<Window>(*this).onResize();//将*this转换成Window,然后调用其onResize;这样不行! 
    } 
};
这里会发生什么问题呢?static_cast<Window>(*this)这个转型动作并不是如你想象的那样得到当前对象的基类对象部分,其实编译器为你产生了的是基类对象的副本。我执行的onResize方法压根就没有执行到基类对象上,而是执行在它的副本上。但是SpecialWindow的专属onResize却执行在子类对象上。使得这个对象处于一种"伤残状态“!

解决方法:

class SpecialWinddow:public Window{ 
public: 
    virtual void onResize(){ 
        Window::onResize();//调用Window::onResize作用于*this身上 
        ... 
    } 
};


(七)

首先,dynamic_cast的执行速度相当慢。

之所以要用dynamic_cast,通常是因为你想在一个你认定为derived class对象身上执行derived class操作函数,但你的手上只有一个“指向base”的pointer或者reference,你只能靠他们来处理对象。但是我们应该尽量避免使用dynamic_cast,有两种替代方法!

第一种方法:

使用容器并在其中存储直接指向derived class对象的指针(通常是智能指针)如此便消除了“通过base class接口处理对象”的需要。

不要像下面这样用dynamic_cast:

class Window{...}; 
class SpecialWindow:public Window { 
public: 
    void blink(); 
}; 
typedef std::vector<std::tr1::shared_ptr<Window>> VPW; 
VPW winPtrs; 
for(VPW::iterator iter = winPtrs.begin(); iter != vinPtrs.end(); ++iter) { 
    if(SpecialWindow* psw = dynamic_cast<SpecialWindow*>(iter->get())) 
        psw->blink(); 
}
要改成这样:

typedef std::vector<std::tr1::shared_ptr<SpecialWindow>> VSPW; 
VSPW winPtrs; 
for(VSPW::iterator iter = winPtrs.begin(); iter != vinPtrs.end(); ++iter) { 
    (*iter)->blink(); 
}
第二种方法:
可以通过base class接口处理“所有可能之各种window派生类”,那就是在base class 里提供virtual函数做你想对各个Window派生类做的事。
class Window { 
public: 
    virtual void blink() {  } 
}; 
class SpecialWindow : public Window { 
public: 
    virtual void blink() {...}; 
}; 
typedef std::vector<std::tr1::shared_ptr<Window>> VPW; 
VPW winPtrs; 
for(VPW::iterator iter = winPtrs.begin(); iter != vinPtrs.end(); ++iter) { 
    (*iter)->blink(); 
}

(八)

绝对必须拒绝的是所谓的“连串(cascading)dynamic_casts”:

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())) {...} 
    if (SpecialWindow2 * psw1 = dynamic_cast<SpecialWindow2*>(iter->get())) {...} 
    if (SpecialWindow3 * psw1 = dynamic_cast<SpecialWindow3*>(iter->get())) {...} 
}
这样的代码应该总是以某些“基于virtual函数调用”的东西取而代之。
优良的c++代码很少使用转型,我们应该尽可能隔离转型动作,通常是把它隐藏在某个函数内,函数的接口会保护调用者不受函数内部任何肮脏龌龊的动作的影响。

 请记住:

(1)如果可以,尽量避免转型,特别是在注重下来的代码中避免dynamic_cast.如果有个设计需要转型动作,试着发展无需转型的替代设计。
(2)如果转型是必要的,试着将它隐藏于某个函数背后.客户随后可以调用该函数,而不需将转型放进它们自己的代码内。

(3)宁可使用C++-style(新式)转型,不要使用旧式转型.前者很容易辨识出来,而且也比较有着分门别类的职掌。





Effective C++:条款27:尽量少做转型动作,布布扣,bubuko.com

Effective C++:条款27:尽量少做转型动作

标签:style   class   blog   code   ext   color   

原文地址:http://blog.csdn.net/u010470972/article/details/33761123

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!