1.static_cast静态类型转换, 用于将一种类型转成另一种类型,并不强求两种类型之间有什么联系,这种转型法比较接近于C语言中的强制转型:(T)value。 dynamic_cast动态类型转换,则只能用于有继承关系之间的类实例指针或引用的相互转型,需要运行期类型识别的支持(RTTI),在继承树之间转型的时候,比static_cast要安全得多。 只有申明了虚拟函数的类才能使用dynamic_cast操作符。多态是通过VPTB实现的。
2. static_cast 在功能上基本上与C风格的类型转换一样强大,含义也一样。它也有功能上限制。例如,你不能用static_cast象用C风格的类型转换一样把struct转换成int类型或者把double类型转换成指针类型,另外,static_cast不能从表达式中去除const属性,因为另一个新的类型转换操作符const_cast有这样的功能。
dynamic_cast,它被用于安全地沿着类的继承关系向下进行类型转换。这就是说,你能用dynamic_cast把指向基类的指针或引用转换成指向其派生类或其兄弟类的指针或引用,而且你能知道转换是否成功。失败的转换将返回空指针(当对指针进行类型转换时)或者抛出异常(当对引用进行类型转换时):即如果dynamic_cast<T*>(foo)失败,返回的是NULL,但是如果是dynamic_cast<T&>(foo)失败,结果是抛出一个bad_cast的异常。
3. dynamic_cast是一个运行期借助于RTTI的操作符,而static_cast不是 。
4. 多继承技术同模板一样,是C++语言中极具争议性的技术。使用多继承技术可以使程序的设计和实现更加灵活,但是,由于多继承的复杂性和自身概念上的一些问题,使多继承在各种面向对象的语言环境中得到的支持都非常有限。例如Small Talk根本就不允许多继承,同样MFC也不支持多继承技术。
多继承最大的问题是所谓的“钻石结构”。例如下面的代码:
class A
{ .....
};
class B : public A
{
.. .
};
class C : public A
{
.....
};
class D : public C,B
{
........
}
由于类D同时从类C和B继承,因此在下面的语句中就会发生歧义:
D* pD = new D;
(A*)pD->Func(...);
由于类D通过类C和类B 分别继承了类A,这里的强制转化就会发生歧义。
ATL使用了C++最新规范中加入的两个运算符号 static_cast、dynamic_cast代替简单的强制转化,从而消除多继承带来的歧义。使用这两个运算符号,我们可以在对象运行过程中获取对象的类型信息。上面的代码可以采用下面的方式修改:
D* pD = new D;
static_cast<A*>(static_cast<B*>(pD))->Func(...);
为什么模板类和多继承技术会成为ATL主要的工具呢?原因在于,采用模板可以在编译过程中快速的生成具有用户定制功能的类,这对于COM这样一个复杂的技术体系在实现效率上得到了很大的提高。通过使用模板类,用户可以把精力集中在自己开发的类的基本逻辑上,在完成了自己的类的设计以后,通过继承不同的类,生成不同的模板类,就可以快速地实现COM的功能,同时又避免了采用单继承结构造成的大量功能冗余。
总之,正是由于在设计实现过程中采用了模板类和多继承技术,才使ATL成为一个小巧灵活的COM开发工具,能够适应开发人员对COM应用开发的各种需要。
5. dynamic_cast的作用是在多态的情况下“还原”一个指针或引用到他真正的类型,而不是将一个A类型“转变”为一个B类型 —— 后者是static_cast的功能。
很难理解是不是?看下面这个小例子:
#include <iostream>
class Base
{
public:
virtual void Display() {std::cout << "Base::Display" << std::endl;}
};
class Derived : public Base
{
public:
virtual void Display() {std::cout << "Derived::Display" << std::endl;}
virtual void MyOwn() {std::cout << "Derived::MyOwn" << std::endl;}
};
int main()
{
Base *b = new Base();
Derived *d;
if ((d = dynamic_cast<Derived *>(b)) != NULL)
{
d->Display();
d->MyOwn();
}
else
{
std::cout << "Cast Failed!" << std::endl;
}
return 0;
}
输出是:Cast Failed!
为什么?因为b是一个 pointer of Base,Base != Derived,所以这个转换就失败了。只有当b是一个“真正的”Derived的时候(也就是这样:Base *b = new Derived(); ),这个转换才会成功。这下明白了吧?你的担心是不存在的,这也就是为什么C++大力倡导新型转换符的原因 —— 它确实让你的程序安全很多。
那么,这种转换有什么作用呢?
举个简单一点的例子吧,假设我有一个类树,基类是TShape,表示所有的类型,派生类有TCircle和TRect,TShape有一个Draw的抽象方法,现在我有一个通用的函数:DrawShape,用于显示Shape,但是显示之前我可能需要设置一些各个具体类的专有参数,于是我就这么写:
void DrawShape(TShape *shape)
{
TCircle *c;
TRect *r;
if ((c = dynamic_cast<TCircle *>(shape)) != NULL)
{
c->SetPoint(10, 20); //设置圆心
c->SetRatio(30); //设置半径
}
else if ((r = dynamic_cast<TRect *>(shape)) != NULL)
{
r->SetPos(10, 20, 30, 40); //设置左上角和右下角的位置
}
shape->Draw(); //调用虚函数进行多态显示
}
这样,只有当shape“真正是一个”TCircle时,才会执行SetPoint和SetRatio,如果不是,就不会执行。这就是dynamic_cast的作用。
但是! 注意我说“但是”,这【不是】一个好的编程风格,正如你看到的,这个DrawShape函数变成了和TCircle、TRect紧密相关,那么以后如果又有一个TTriangle类怎么办?这不符合多态的精神,所以这种风格是C++中极力避免的。基于这个原因,dynamic_cast也是应该极力避免的,C++不像Java,C++不提倡RTTI。事实上,这些问题都可以通过精致的设计来避免。至于如何“精致的设计”,可以看看《设计模式》,这是一本真正的好书。
当然,彻底避免dynamic_cast也不是什么好事,这常常会导致“过度设计”,为软件带来不必要的复杂性和冗余度。如何在灵活性和复杂度之间取舍,就要依靠一个优秀的程序员(或者说软件设计师:) )的经验了。
有几本书可以参考:《Effective C++》、《More Effective C++》、《Design Pattern》、《The Design & Evolution of C++》(有人奇怪为什么我会推荐这本看起来好像历史书的小册子,其实里面对C++这门语言做了非常细致的讨论,为什么要这样,为什么不要那样,里面都有提到,这对理解C++的许多问题是很有好处的,我看这本书中讨论的问题时,发现许多问题我从来连想都没想到过)
6. reinterpret_cast可以转换任意一个32bit整数,包括所有的指针和整数。可以把任何整数转成指针,也可以把任何指针转成整数,以及把指针转化为任意类型的指针,威力最为强大!但不能将非32bit的实例转成指针。总之,只要是32bit的东东,怎么转都行!
static_cast和dynamic_cast可以执行指针到指针的转换,或实例本身到实例本身的转换,但不能在实例和指针之间转换。static_cast只能提供编译时的类型安全,而dynamic_cast可以提供运行时类型安全。举个例子:
class A;class B:A;class C。
上面三个类A是基类,B继承A,C和AB没有关系。
有一个函数void function(A&a);
现在有一个对象是B的实例b,一个C的实例c。
function(static_cast<A&>(b)可以通过而function(static<A&>(c))不能通过编译,因为在编译的时候编译器已经知道c和a的类型不符,因此static_cast可以保证安全。
下面我们骗一下编译器,先把c转成类型a
B& ref_b = reinterpret_cast<B&>c;
然后function(static_cast<A&>(ref_b))就通过了!因为从编译器的角度来看,在编译时并不能知道ref_b实际上是c!
而function(dynamic_cast<A&>(ref_b))编译时也能过,但在运行时就失败了,因为dynamic_cast在运行时检查了ref_b的实际类型,这样怎么也骗不过去了。
在应用多态编程时,当我们无法确定传过来的对象的实际类型时使用dynamic_cast,如果能保证对象的实际类型,用static_cast就可以了。