dynamic_cast在什么时候使用:
使用时间:子类和父类之间的多态类型转换
引入dynamic_cast的原因:
举例说明:
#include <iostream>
using namespace std;
class Base
{
public:
virtual void BaseAAA()
{
cout<<"BaseAAA"<<endl;
}
virtual void AAA()
{
cout<<"Base::VirtualAAA"<<endl;
}
};
class Derived : public Base
{
public:
void DerivedAAA()
{
cout<<"DerivedAAA"<<endl;
}
void AAA()
{
cout<<"Derived::VirtualAAA"<<endl;
}
};
int main()
{
//为了实现虚函数的调用,让基类指针指向了派生类对象。
Base* pBaseOne = new Derived;
//使用基类指针pBaseOne,可以访问虚函数,实现多态
pBaseOne->AAA(); //输出Derived::VirtualAAA
//但是问题来了,让基类指针指向派生类后,基类指针无法访问派生类的成员了。
//pBaseOne->DerivedAAA();//操作失败
//这时需要对这个基类指针进行强制类型转换,获得派生类对象,之后操作派生类自己的成员
Derived* pDerived = dynamic_cast<Derived*>(pBaseOne);
pDerived->DerivedAAA(); //输出DerivedAAA
system("pause");
return 1;
}
具体来说:
为了实现多态,让基类指针指向了派生类对象,但是这个基类指针无法操作派生类自己的成员了,所以就想把这个指针由基类指针转换为派生类指针,此时就可以使用dynamic_cast。即,已经有了派生类指针转换为基类指针。之后,使用dynamic_cast,把基类指针转换派生类指针。
dynamic_cast的作用:
作用:将基类类型的指针或引用安全地转换为派生类型的指针或引用。
注意事项:
(0)在dynamic_cast中,dynamic表示在类型转换时,是在运行时进行的,其与Static对应。
(1)使用dynamic_cast转换时,在expression中必有虚函数,否则编译会出错。而Static_cast没有这个限制。但是即使原来的参数和目标参数之间没有继承关系时,编译器不会报错。
原因:在类中存在虚函数时,才会有可能出现让基类指针或引用指向派生类对象的情况(虚函数使用的前提),此时转换才有意义。或者说,dynamic_cast操作时专门针对由虚函数的继承来的,它将基类指针转换为想要的子类指针,以做好操作子类中有而父类中没有的操作。由于,判断类是否有继承关系,是需要检测是否有虚函数,即在原来的参数和目标参数之间没有继承关系时,编译器不会报错,因为它由虚函数。
dynamic_cast的语法:
dynamic_cast<type-id>(expression)
说明:
(1) type_id 和 目标类型是一样的
(2) type_id 和 目标类型是引用或指针。
举例:
Derived* pDerived = dynamic_cast<Derived*>(pBaseOne);//成功,返回Derived的指针,失败返回NULL
Base& BaseClass = dynamic_cast<Base&>(DerivedClass); //成功,返回Base的引用,失败抛出bad_cast异常
使用dynamic_cast转换时,会执行两个操作:
(1)它首先验证被请求的转换是否有效,这是在编译时判断,检测expression代表的类中是否有虚函数。
(2)对操作数进行类型转换。这是在运行是判断。
dynamic_cast的分类:
dynamic_cast要求要转换的两个值是有关系的,如果转型成功,则返回转换过的指针,如果转换不成功,返回NULL。
根据类之间的关系,可以分三种情况:
(1)上行转换:一个子类对象的指针/引用转换为基类的指针/引用(子类到基类的转换)
(2)下行转换:基类的指针/引用转换为一个子类的指针/引用(基类到子类的转换)
(3)交叉转换:多个基类,不同子类之间的相互转换
下面分情况说明这三种情况:
(1)上行转换:一个子类对象的指针/引用转换为基类的指针/引用(子类到基类的转换)
说明:这里dynamic_cast和static_cast以及直接赋值的效果是一样的。
举例:
#include <iostream>
using namespace std;
class Base
{
public:
virtual void BaseAAA()
{
cout<<"BaseAAA"<<endl;
}
virtual void AAA()
{
cout<<"BaseAAA::VirtualAAA"<<endl;
}
};
class Derived : public Base
{
public:
virtual void DerivedAAA()
{
cout<<"DerivedAAA"<<endl;
}
virtual void AAA()
{
cout<<"Derived::VirtualAAA"<<endl;
}
};
int main()
{
Derived* pD = new Derived;
//正常情况-dynamic_cast
Base* pB = dynamic_cast<Derived*>(pD);
pB->BaseAAA(); //输出BaseAAA
pB->AAA(); //输出Derived::VirtualAAA
//正常情况-static_cast
Base* pBB = static_cast<Derived*>(pD);
pBB->BaseAAA(); //输出BaseAAA
pBB->AAA(); //输出Derived::VirtualAAA
//不要使用转换
Base* pBBB = pD;
pBBB->BaseAAA(); //输出BaseAAA
pBBB->AAA(); //输出Derived::VirtualAAA
system("pause");
return 1;
}
(2)下行转换:基类的指针/引用转换为一个子类的指针/引用(基类到子类的转换)
说明:这是dynamic_cast的特长。比static_cast安全。
原因:dynamic_cast能够在运行是对类型进行检查,如果绑定到引用或指针的对象不是目标类型的对象,则dynamic_cast 失败。
dynamic_cast在什么时候能成功?
如果基类指针或引用本来原来是指向或引用一个派生类的情况下,会成功。
失败后的行为:
使用dynamic_cast转换:
如果,基类指针或引用的值在转换前就是指向或引用派生类的情况下,则在转换后,派生类指针或引用就重新获得了一个派生类指针或引用。
如果,基类指针或引用的值在转换前就是指向或引用基类的情况下,在语句执行时,会立即出现错误,并返回不同的状态。
dynamic_cast失败后的状态:
如果,待转换的参数为指针类型,则返回NULL。
如果,待转换的参数为引用类型,则抛出一个bad_cast 类型的异常。
使用static_cast转换:
如果,基类指针或引用的值在转换前就是指向或引用派生类的情况下,则在转换后,派生类指针或引用就重新获得了一个派生类指针或引用。
如果,基类指针或引用的值在转换前就是指向或引用基类的情况下,在语句执行时,不会立即出现错误,而是会在使用的时候才会出现错误。
static_cast失败的情况:
如果,使用转换后的指针或引用调用了派生类的成员,则会报错。
如果,仍然调用基类(自己)的成员,则不会报错。即会隐含错误。
举例:
#include <iostream>
using namespace std;
class Base
{
public:
virtual void BaseAAA()
{
cout<<"BaseAAA"<<endl;
}
virtual void AAA()
{
cout<<"BaseAAA::VirtualAAA"<<endl;
}
};
class Derived : public Base
{
public:
virtual void DerivedAAA()
{
cout<<"DerivedAAA"<<endl;
}
virtual void AAA()
{
cout<<"Derived::VirtualAAA"<<endl;
}
};
int main()
{
Derived* pD = NULL;
Base* pB = new Derived;
//正常情况-dynamic_cast
pD = dynamic_cast<Derived*>(pB);
pD->DerivedAAA(); //输出DerivedAAA
pD->AAA(); //输出Derived::VirtualAAA
//正常情况-static_cast
Derived* pD1 = static_cast<Derived*>(pB);
pD1->DerivedAAA(); //输出DerivedAAA
pD1->AAA(); //输出Derived::VirtualAAA
Base* pBB = new Base;
//失败情况
Derived* pDD = dynamic_cast<Derived*>(pBB);//此时pDD返回NULL
//pDD->DerivedAAA(); //出错
//pDD->AAA(); //出错
//失败情况-static_cast
Derived* pDD1 = static_cast<Derived*>(pBB);
//pDD1->DerivedAAA(); //出错-用到不是自己的成员时,且该成员是虚函数时才报错。
pDD1->AAA(); //输出Base::VirtualAAA
system("pause");
return 1;
}
B要有虚函数,否则会编译出错;static_cast则没有这个限制。
B中需要检测有虚函数的原因:类中存在虚函数,就说明它有想要让基类指针或引用指向派生类对象的情况,此时转换才有意义。
这是由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表(关于虚函数表的概念,详细可见<Inside c++ object model>)中,只有定义了虚函数的类才有虚函数表,没有定义虚函数的类是没有虚函数表的。
(3)交叉转换:多个基类,不同子类之间的相互转换
说明:dynamic_cast可以做,但是static_cast不可以做,编译都通不过。
举例:
dynamic_cast在什么时候能成功转换?
如果一个派生类DerivedTwo是由两个基类BaseOne和BaseTwo派生。
而且把DerivedTwo对象的指针给了其中一个基类BaseOne,那么可以对BaseOne的指针进行dynamic_cast转换,转换为BaseTwo的指针。
BaseOne* pBaseOne = new DerivedTwo;
BaseTwo* pBaseTwo = dynamic_cast<BaseTwo*>(pBaseOne);
pBaseTwo->AAA(); //正确执行
pBaseTwo->BaseTwoAAA(); //正确执行
为什么dynamic_cast能成功转换?
这是由于在定义指针pBaseOne的指向时,其指向的内容本来就包含两个基类的内容,所以可以转换成功。
给出不成功的情况:
BaseOne* pBaseOne = new DerivedOne;
BaseTwo* pBaseTwo = dynamic_cast<BaseTwo*>(pBaseOne);
pBaseTwo->AAA();//报错
pBaseTwo->BaseTwoAAA();//报错
dynamic_cast不能转换的原因?
这是由于在定义指针pBaseOne的指向时,其指向的内容只包含一个基类BaseOne的内容,不含有BaseTwo的内容,所以不能成功,此时pBaseTwo的值为NULL。
举例:
#include <iostream>
using namespace std;
class BaseOne
{
public:
virtual void BaseOneAAA()
{
cout<<"BaseOneAAA"<<endl;
}
virtual void AAA()
{
cout<<"BaseOne::VirtualAAA"<<endl;
}
};
class BaseTwo
{
public:
virtual void BaseTwoAAA()
{
cout<<"BaseTwoAAA"<<endl;
}
virtual void AAA()
{
cout<<"BaseTwo::VirtualAAA"<<endl;
}
};
class DerivedOne : public BaseOne
{
public:
virtual void DerivedOneAAA()
{
cout<<"DerivedOneAAA"<<endl;
}
void AAA()
{
cout<<"DerivedOne::VirtualAAA"<<endl;
}
};
class DerivedTwo : public BaseOne,public BaseTwo
{
public:
virtual void DerivedTwoAAA()
{
cout<<"DerivedTwoAAA"<<endl;
}
void AAA()
{
cout<<"DerivedTwo::VirtualAAA"<<endl;
}
};
int main()
{
BaseOne* pBaseOne = NULL;
BaseTwo* pBaseTwo = NULL;
//dynamic_cast转换成功的情况
pBaseOne = new DerivedTwo;
pBaseTwo = dynamic_cast<BaseTwo*>(pBaseOne);
pBaseTwo->AAA(); //输出DerivedTwo::VirtualAAA
pBaseTwo->BaseTwoAAA(); //输出BaseTwoAAA
//dynamic_cast转换失败的情况
pBaseOne = new DerivedOne;
pBaseTwo = dynamic_cast<BaseTwo*>(pBaseOne);
pBaseTwo->AAA();
pBaseTwo->BaseTwoAAA();
static_cast转换失败的情况
直接是编译不通过:static_cast无法实现BaseOne* 到BaseTwo* 的转换。
//pBaseOne = new DerivedOne;
//pBaseTwo = static_cast<BaseTwo*>(pBaseOne);
//pBaseTwo->AAA();
//pBaseTwo->BaseTwoAAA();
//pBaseOne = new DerivedTwo;
//pBaseTwo = static_cast<BaseTwo*>(pBaseOne);
//pBaseTwo->AAA();
//pBaseTwo->BaseTwoAAA();
system("pause");
return 1;
}
参考文章:http://www.cnblogs.com/weidagang2046/archive/2010/04/10/1709226.html