传统的A* a = (A*)p强制类型转换相当于static_cast,得到的总是一个内存地址,而dynamic_cast则更安全,转换失败会得到NULL。
dynamic_cast和static_cast区别用如下例子程序非常容易理解:
class Base
{
public:
virtual ~Base(){};
};
class A : public Base
{
};
class B : public Base
{
};
int main()
{
Base *a = new A();
Base *b = new B();
if (A *p = dynamic_cast<A*>(b)) {
cout << "A instance" << endl;
}
if (B *p = dynamic_cast<B*>(a)) {
cout << "B instance" << endl;
}
return 0;
}
上面例子不会打印出任何内容,将dynamic_cast后的a和b进行掉换就能正确打印了。
改成static_cast也是能正确打印的,但会是危险的,因为明显a强制转换成了B*指针,而b强制转换成了A*指针:
int main()
{
Base *a = new A();
Base *b = new B();
if (A *p = static_cast<A*>(b)) {
cout << "A instance" << endl;
}
if (B *p = static_cast<B*>(a)) {
cout << "B instance" << endl;
}
return 0;
}
有了dynamic_cast这个特性,我们就可以用来进行类型识别了,将基类指针依次尝试转换成各种子类指针。
特别注意:基类Base里必须要有虚函数,任何虚函数都可以,不一定要是虚析构函数。否则编译会不过:
error: cannot dynamic_cast ‘b’ (of type ‘class Base*’) to type ‘class A*’ (source type is not polymorphic)
if (A *p = dynamic_cast<A*>(b)) {
可见虚函数表,即vtable,里有存储类继承关系信息。获取vtable信息,可以使用如下命令:
g++ -fdump-class-hierarchy test.cpp -std=c++11
可查生成的文件test.cpp.002t.class,里面有简单的类信息和vtable信息,截取如下:
Vtable for A
A::_ZTV1A: 3u entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI1A)
16 (int (*)(...))Base::foo
Class A
size=8 align=8
base size=8 base align=8
A (0x0x7f3a51579c30) 0 nearly-empty
vptr=((& A::_ZTV1A) + 16u)
Base (0x0x7f3a513ab000) 0 nearly-empty
primary-for A (0x0x7f3a51579c30)