static_cast
资料上说:static_cast可以用于有继承关系的子类类型向父类类型的转换,但相反的转换则不可以。究其原因,应该是子类对象中包含父类成分,而父类对象中不包含子类成分。从子类向父类的转换就像是高精度向低精度转换,可能会丢失精度,但不影响使用;而从父类向子类的转换就像是低精度向高精度转换,丢失的内容不会补全,影响使用。
class Base{};
class Derived: public Base{};
Derived child1;
Base parent1 = static_cast<Base>(child1); //可以
Base parent2;
Derived child2 = static_cast<Derived>(parent2); //不可以
然而,如果是指针类型或者引用类型,则相互之间使用static_cast进行转换是可以的。这属于C++风格的强制类型转换。
class Base{
public:
int a = 1;
virtual void print(){cout<<"Base::print(): "<<a<<endl;>}
};
class Derived: public Base{
public:
int b = 2;
void print()override{cout<<"Derived::print(): "<<b<<endl;>}
};
Derived *child1 = new Derived();
Base *parent1 = static_cast<Base*>(child1); //可以
parent1->print(); //Derived::print()
Base *parent2 = new Base();
Derived *child2 = static_cast<Derived*>(parent2); //可以
child2->print(); //Base::print()
cout<<child2->b<<endl; // -33686019
上述代码中,Base *parent1 = static_cast<Base*>(child1);将子类指针转换为父类指针,与正常情况下Base *parent = new Derived();(即使用父类指针指向子类对象)的表现并无二致。事实上,编译器在内部为后者隐式执行了类型转换。同时,在虚函数机制的作用下,parent1->print()输出了子类重载的虚函数。
如果按照上述理解,Derived *child2 = static_cast<Derived*>(parent2);将父类指针转换为子类指针就应该与正常情况下Derived *child = new Base();(即使用子类指针指向父类对象)的表现并无二致。然而,事实是正常情况下Derived *child = new Base();会报无法转换的编译错误。而使用static_cast居然转换成功了,且没有编译错误,程序也能运行。同时,child2->print();可以输出父类的虚函数。但是,这里是否可以理解成是虚函数机制在发挥作用吗?通过比较child2->print()与parent1->print()的反汇编代码,我认为虚函数机制仍然发挥了作用。但因为父类中没有包含子类成分,所以child2->b的输出有问题。因此,这种将父类指针或引用转换为子类指针或引用的向下类型转换行为是不安全的。
dynamic_cast
多态情况下,可以使用父类指针或引用指向子类对象,这就为编程提供了统一接口。那么在接口内部应该判断父类指针或引用指向的是否是子类对象,否则可能会出现上述不安全的转换行为。dynamic_cast可以用来解决这个问题。该运算符使用RTTI机制,即运行时类型识别。它会进行类型检查,如果父类指针没有指向子类对象,则转换失败,返回空指针。如果是引用类型,则要捕获std::bad_cast异常。
void DynamicCastTest(Base *parent)
{
auto realType = dynamic_cast<Derived*>(parent);
if(realType!=nullptr){
cout<<"The real type is class Derived"<<endl;
}
else{
cout<<"The real type is class Parent"<<endl;
}
}
void DynamicCastTest2(Base &parent)
{
try{
auto &realType = dynamic_cast<Derived&>(parent);
}
catch(std::bad_cast){
//......
}
}