thanks:http://www.cnblogs.com/chio/archive/2007/07/18/822389.html
回顾一下C++类型转换:
C++类型转换分为:隐式类型转换和显式类型转换
又称为“标准转换”,包括以下几种情况:
1) 算术转换(Arithmetic conversion) : 在混合类型的 算术表达式 中, 最宽的数据类型成为目标转换类型。
double dval = 3.14159 ;
ival + dval; // ival被提升为double类型
2)一种类型表达式赋值给另一种类型的对象:目标类型是被赋值对象的类型
ival = dval; // double->int
例外:void指针赋值给其他指定类型指针时,不存在标准转换,编译出错
3)将一个表达式作为实参传递给函数调用,此时形参和实参类型不一致:目标转换类型为形参的类型
cout << " The square root of 2 is " << sqrt( 2 ) << endl;
// 2被提升为double类型:2.0
4)从一个函数返回一个表达式,表达式类型与返回类型不一致:目标转换类型为函数的返回类型
{
return ival1 - ival2;
// 返回值被提升为double类型
}
被称为“强制类型转换”(cast)
C 风格: (type-id)
C++风格: static_cast 、 dynamic_cast 、 reinterpret_cast 、和 const_cast..
关于强制类型转换的问题,很多书都讨论过,写的最详细的是C++ 之父的《C++ 的设计和演化》。最好的解决方法就是不要使用C风格的强制类型转换,而是使用标准C++的类型转换符:static_cast, dynamic_cast。标准C++中有四个类型转换符:
static_cast、
dynamic_cast、
reinterpret_cast、和
const_cast。下面对它们一一进行介绍。
static_cast
用法:
static_cast < type-id > ( expression )
说明:该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性。
来源:为什么需要static_cast强制转换?
情况1:void指针->其他类型指针 情况2:改变通常的标准转换 情况3:避免出现可能多种转换的歧义 它主要有如下几种用法:
dynamic_cast
用法:
dynamic_cast < type-id > ( expression )
说明:该运算符把expression转换成type-id类型的对象。Type-id必须是类的指针、类的引用或者void *;如果type-id是类指针类型,那么expression也必须是一个指针,如果type-id是一个引用,那么expression也必须是一个引用。
|
thanks:http://blog.csdn.net/insistgogo/article/details/8941513
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中需要检测有虚函数的原因:类中存在虚函数,就说明它有想要让基类指针或引用指向派生类对象的情况,此时转换才有意义。
这是由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表(关于虚函数表的概念,详细可见<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://blog.csdn.net/ss0429/article/details/8929341
dynamic_cast <new_type> (expression) 动态转换
动态转换确保类指针的转换是合适完整的,它有两个重要的约束条件,
其一是要求new_type为指针或引用,其二是下行转换时要求基类是多态的(基类中包含至少一个虚函数)。
看一下下面的例子:
- #include <iostream>
- using namespace std;
- class CBase { };
- class CDerived: public CBase { };
- int main() {
- CBase b; CBase* pb;
- CDerived d; CDerived* pd;
- pb = dynamic_cast<CBase*>(&d); // ok: derived-to-base
- pd = dynamic_cast<CDerived*>(&b); // wrong: base-to-derived
- }
在最后一行代码有问题,编译器给的错误提示如下图所示:
把类的定义改成:
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(空指针)。
再举一个例子:
- #include <iostream>
- #include <exception>
- using namespace std;
- class CBase { virtual void dummy() {} };
- class CDerived: public CBase { int a; };
- int main () {
- try {
- CBase * pba = new CDerived;
- CBase * pbb = new CBase;
- CDerived * pd;
- pd = dynamic_cast<CDerived*>(pba);
- if (pd==0) cout << "Null pointer on first type-cast" << endl;
- pd = dynamic_cast<CDerived*>(pbb);
- if (pd==0) cout << "Null pointer on second type-cast" << endl;
- } catch (exception& e) {cout << "Exception: " << e.what();}
- return 0;
- }
输出结果是:Null pointer on second type-cast
两个dynamic_cast都是下行转换,第一个转换是安全的,因为指向对象的本质是子类,转换的结果使子类指针指向子类,天经地义;
第二个转换是不安全的,因为指向对象的本质是父类,“指鹿为马”或指向不存在的空间很可能发生!
最后补充一个特殊情况,当待转换指针是void*或者转换目标指针是void*时,dynamic_cast总是认为是安全的,举个例子:
- #include <iostream>
- using namespace std;
- class A {virtual void f(){}};
- class B {virtual void f(){}};
- int main() {
- A* pa = new A;
- B* pb = new B;
- void* pv = dynamic_cast<void*>(pa);
- cout << pv << endl;
- // pv now points to an object of type A
- pv = dynamic_cast<void*>(pb);
- cout << pv << endl;
- // pv now points to an object of type B
- }
运行结果如下:
可见dynamic_cast认为空指针的转换安全的,但这里类A和类B必须是多态的,包含虚函数,若不是,则会编译报错。
reinterpret_cast <new_type> (expression) 重解释转换
这个转换是最“不安全”的,两个没有任何关系的类指针之间转换都可以用这个转换实现,举个例子:
- class A {};
- class B {};
- A *a = new A;
- B *b = reinterpret_cast<B*>(a);//correct!
更厉害的是,reinterpret_cast可以把整型数转换成地址(指针),这种转换在系统底层的操作,有极强的平台依赖性,移植性不好。
它同样要求new_type是指针引或用,下面的例子是通不过编译的:
- double a=2000.3;
- short b;
- b = reinterpret_cast<short> (a); //compile error!
const_cast <new_type> (expression) 常量向非常量转换
这个转换好理解,可以将常量转成非常量。
- // const_cast
- #include <iostream>
- using namespace std;
- void print (char * str) {
- cout << str << endl;
- }
- int main () {
- const char * c = "sample text";
- char *cc = const_cast<char *> (c) ;
- Print(cc);
- return 0;
- }
从char *cc = const_cast<char *>(c)可以看出了这个转换的作用了,但切记,
这个转换并不转换原常量本身,即c还是常量,只是它返回的结果cc是非常量了。
总结
C风格转换是“万能的转换”,但需要程序员把握转换的安全性,编译器无能为力;static_cast最接近于C风格转换,但在无关类指针转换时,编译器会报错,提升了安全性;dynamic_cast要求转换类型必须是指针或引用,且在下行转换时要求基类是多态的,如果发现下行转换不安全,dynamic_cast返回一个null指针,dynamic_cast总是认为void*之间的转换是安全的;reinterpret_cast同样要求转换类型必须是指针或引用,可以对无关类指针进行转换,甚至可以直接将整型值转成指针,这种转换是底层的,有较强的平台依赖性,可移植性差;const_cast可以将常量转成非常量,但不会破坏原常量的const属性,只是返回一个去掉const的变量。