EffectiveC++,实现(2)static_cast,dynamic_cast,const_static,reinterpret_cast

来源:互联网 发布:windows网络发现服务 编辑:程序博客网 时间:2024/05/19 23:27

尽量少做转型动作-大量使用转型可能导致代码变慢并且不容易维护。

C++设计规则之一就是保证“类型错误”绝不可能发生,因此在进行编码时不要轻易放弃不干净的编译,从而阻止在任何对象上执行的不安全的、无意义的操作。

转型破坏了类型系统,可能会带来不必要的麻烦,首先列举转型语法。
旧式转型(old-style casts):
(T)expression;T(expression)--功能相同都是将expression转型为T类型。
新式转型(new-style casts):
const_cast<T>(expression)
dynamic_cast<T>(expression)
reinterpret_cast<T>(expression)
static_cast<T>(expression)

各类型的功能:
const_cast<T>(expression):
用来移除对象的常量特性,也是唯一拥有此功能的C++类型转换符。
(1)常量指针被转化成非常量的指针,并且仍然指向原来的对象;
(2)常量引用被转换成非常量的引用,并且仍然指向原来的对象;
(3)const_cast一般用于修改底指针。如const char *p形式。
例:

class CTmp{public:    explicit CTmp(){}    ~CTmp(){}public:    int m_num;};void main(){    const CTmp tmp1;    //tmp1.m_num = 1; //由于tmp1为const类型,因此赋值错误      CTmp *tmp2 = const_cast<B*>(&b1);//指针类型转换    CTmp &tmp3 = const_cast<B&>(b1);//常量类型转换   //可以进行正常操作   tmp2->m_num = 2;      tmp3.m_num = 3;   }

dynamic_cast<T>(expression):

唯一无法由旧式语法执行的动作,唯一可能耗费重大运行成本的转型动作。
(1)运算符把expression转换成T类型的对象。T必须是类的指针、类的引用或者void*。
(2)基类必须有虚函数,否则无法通过编译。因为类型检查需要运行时的类型信息,当前信息存储在虚函数表内,假如不定义虚函数则无虚函数表。因此无法进行类型检查。
(3)主要应用于类层次间的上行转换和下行转换,还可以用于类间的交叉转换。上行转换时功能同static_cast相同,下行转换时dynamic_cast具有类型检查功能,可进行安全转换,失败返回空指针。

class CTmp1{public:virtual void tmpFun();}; class CTmp2:public CTmp1{public:}; void func(CTmp1* tmpV){   CTmp2* T1=static_cast<CTmp2*>(tmpV);   CTmp2* T2=dynamic_cast<CTmp2*>(tmpV);}
上边代码,如果tmpV是CTmp2类型对象,那么两种类型转换是相同的,并且都是安全的。如果tmpV是CTmp1类型的对象,则T1为一个异常指针,对访问类成员不安全,但是T2获得空指针。
交叉转换

class CTmp1{public:virtual void F(){}};class CTmp2 : public CTmp1{ };class CTmp3 : public CTmp1{};void main(){CTmp2* T = new CTmp2 ;CTmp3 *pd1=static_cast<CTmp3 *>(T);//无法进行转换CTmp3 *pd2=dynamic_cast<CTmp3 *>(T);//返回空指针delete T;}
reinterpret_cast<T>(expression):
实际动作取决于编译器,因此他无法进行移植。很少使用


static_cast<T>(expression):
进行强迫隐式类型转换,无法保证安全性。
non-const转换为const对象,int,double等类型转换。但是无法将const转换为non-const,只有const_cast能够实现。


当前新式类型转换更加受欢迎,1、代码出问题时容易发现问题所在 2、有自身功能,编译器可方便进行错误诊断。


类型转换实例:
对于以下代码,在当前对象的副本上调用Window::onResize函数,然后再当前对象上执行SpecialWindow专属动作。若此时Window::onResize修改成员变量内容,但不是在当前对象上进行的修改而是在副本上,但SpecialWindow::onResize却是在当前对象上进行的内容修改。造成基类修改未完成,但是派生类确完成了修改。
class Window{public:Window(){}~Window(){}virtual void onResize(){yui =0;}int yui;};class SpecialWindow : public Window{public:SpecialWindow(){}~SpecialWindow(){}virtual void onResize(){static_cast<Window>(*this).onResize();//无法再当前对象调用Window::onResize();//在当前对象身上进行调用        ui = 0;}int ui;};int _tmain(int argc, _TCHAR* argv[]){SpecialWindow *tyu = new SpecialWindow;tyu->ui = 9;tyu->yui = 67;tyu->onResize();return 0;}
dynamic_cast执行速度慢,对其应用主要在派生类对象身上执行派生类函数时,可用资源只有指向基类的指针和引用,一般性更改的两个方法。


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


通过容器存储派生类指针,消除通过基类借口处理对象的需要。为防止多容器定义,可在Window中只定义接口,而在派生类中实现动作,这样在定义容器时候就可以定义基类的容器从而可通过一个容器存放所有的派生类。


例如连串dynamic_casts操作造成相关问题:
class Window{};typedef  vector<tr1::shared_ptr<Window>>VPW;class SW1 : public Window{};class SW2 : public Window{};class SW3 : public Window{};VPW winPtrs;for(VPW::iterator iter=winPtrs.begin();iter!=winPtrs.end;++iter){    if(SW1* pw1 = dyanamic_cast<SW1*>(iter->get()))    {    }else if(SW1* pw2 = dyanamic_cast<SW2*>(iter->get()))    {    }else if(SW1* pw3 = dyanamic_cast<SW3*>(iter->get()))    {    }}
如此产生的代码大并且慢,因为一旦Window class 继承体系有改变,例如加入新的派生类或者条件分支改动,都必须再次查看for循环代码是否需要修改。


0 0