dynamic_cast <new_type> (expression) 动态转换
动态转换确保类指针的转换是合适完整的,它有两个重要的约束条件,其一是要求new_type为指针或引用,其二是下行转换时要求基类是多态的(基类中包含至少一个虚函数)。
看一下下面的例子:
1 #include <iostream> 2 using namespace std; 3 class CBase { }; 4 class CDerived: public CBase { }; 5 6 int main() 7 { 8 CBase b; CBase* pb; 9 CDerived d; CDerived* pd; 10 11 pb = dynamic_cast<CBase*>(&d); // ok: derived-to-base 12 pd = dynamic_cast<CDerived*>(&b); // wrong: base-to-derived 13 }
在最后一行代码有问题,编译器给的错误提示如下图所示:
把类的定义改成:
class CBase { virtual void dummy() {} };
class CDerived: public CBase {};
再编译,结果如下图所示:
编译都可以顺利通过了。这里我们在main函数的最后添加两句话:
cout << pb << endl;
cout << pd << endl;
输出pb和pd的指针值,结果如下:
我们看到一个奇怪的现象,将父类经过dynamic_cast转成子类的指针竟然是空指针!这正是dynamic_cast提升安全性的功能,dynamic_cast可以识别出不安全的下行转换,但并不抛出异常,而是将转换的结果设置成null(空指针)。
再举一个例子:
1 #include <iostream> 2 #include <exception> 3 using namespace std; 4 5 class CBase { virtual void dummy() {} }; 6 class CDerived: public CBase { int a; }; 7 8 int main () { 9 try { 10 CBase * pba = new CDerived; 11 CBase * pbb = new CBase; 12 CDerived * pd; 13 14 pd = dynamic_cast<CDerived*>(pba); 15 if (pd==0) cout << "Null pointer on first type-cast" << endl; 16 17 pd = dynamic_cast<CDerived*>(pbb); 18 if (pd==0) cout << "Null pointer on second type-cast" << endl; 19 20 } catch (exception& e) {cout << "Exception: " << e.what();} 21 return 0; 22 }
输出结果是:Null pointer on second type-cast
两个dynamic_cast都是下行转换,第一个转换是安全的,因为指向对象的本质是子类,转换的结果使子类指针指向子类,天经地义;第二个转换是不安全的,因为指向对象的本质是父类,“指鹿为马”或指向不存在的空间很可能发生!
最后补充一个特殊情况,当待转换指针是void*或者转换目标指针是void*时,dynamic_cast总是认为是安全的,举个例子:
1 #include <iostream> 2 using namespace std; 3 class A {virtual void f(){}}; 4 class B {virtual void f(){}}; 5 6 int main() { 7 A* pa = new A; 8 B* pb = new B; 9 void* pv = dynamic_cast<void*>(pa); 10 cout << pv << endl; 11 // pv now points to an object of type A 12 13 pv = dynamic_cast<void*>(pb); 14 cout << pv << endl; 15 // pv now points to an object of type B 16 }
运行结果如下:
可见dynamic_cast认为空指针的转换安全的,但这里类A和类B必须是多态的,包含虚函数,若不是,则会编译报错。