C++中的四种强制转换类型的有什么不同,或者是说用法的不同?
dynamic_cast<>,const_cast<>,static_cast<>,reinterprer_cast<>的不同?
修改了其中的一处笔误:reinterpret_cast 错写成reinpreter_cast
使用标准C++的类型转换符:static_cast、dynamic_cast、reinterpret_cast、和const_cast。
1 static_cast
用法:static_cast < type-id > ( expression )
该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性。它主要有如下几种用法:
①用于类层次结构中基类和子类之间指针或引用的转换。
进行上行转换(把子类的指针或引用转换成基类表示)是安全的;
进行下行转换(把基类指针或引用转换成子类表示)时,由于没有动态类型检查,所以是不安全的。
②用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。
③把空指针转换成目标类型的空指针。
④把任何类型的表达式转换成void类型。
注意:static_cast不能转换掉expression的const、volitale、或者__unaligned属性。
2 dynamic_cast
用法:dynamic_cast < type-id > ( expression )
该运算符把expression转换成type-id类型的对象。Type-id必须是类的指针、类的引用或者void *;
如果type-id是类指针类型,那么expression也必须是一个指针,如果type-id是一个引用,那么expression也必须是一个引用。
dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。
在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;
在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。
class B{
public:
int m_iNum;
virtual void foo();
};
class D:public B{
public:
char *m_szName[100];
};
void func(B *pb){
D *pd1 = static_cast<D *>(pb);
D *pd2 = dynamic_cast<D *>(pb);
}
在上面的代码段中,如果pb指向一个D类型的对象,pd1和pd2是一样的,并且对这两个指针执行D类型的任何操作都是安全的;
但是,如果pb指向的是一个B类型的对象,那么pd1将是一个指向该对象的指针,对它进行D类型的操作将是不安全的(如访问m_szName),
而pd2将是一个空指针。
另外要注意:B要有虚函数,否则会编译出错;static_cast则没有这个限制。
这是由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表(
关于虚函数表的概念,详细可见<Inside c++ object model>)中,只有定义了虚函数的类才有虚函数表,
没有定义虚函数的类是没有虚函数表的。
另外,dynamic_cast还支持交叉转换(cross cast)。如下代码所示。
class A{
public:
int m_iNum;
virtual void f(){}
};
class B:public A{
};
class D:public A{
};
void foo(){
B *pb = new B;
pb->m_iNum = 100;
D *pd1 = static_cast<D *>(pb); //compile error
D *pd2 = dynamic_cast<D *>(pb); //pd2 is NULL
delete pb;
}
在函数foo中,使用static_cast进行转换是不被允许的,将在编译时出错;而使用 dynamic_cast的转换则是允许的,结果是空指针。
3 reinterpret_cast
用法:reinterpret_cast<type-id> (expression)
type-id必须是一个指针、引用、算术类型、函数指针或者成员指针。
它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,
在把该整数转换成原类型的指针,还可以得到原先的指针值)。
该运算符的用法比较多。
(static_cast .与. reinterpret_cast比较,见下面 )
4 const_cast
用法:const_cast<type_id> (expression)
该运算符用来修改类型的const或volatile属性。除了const 或volatile修饰之外, type_id和expression的类型是一样的。
常量指针被转化成非常量指针,并且仍然指向原来的对象;
常量引用被转换成非常量引用,并且仍然指向原来的对象;常量对象被转换成非常量对象。
Voiatile和const类试。举如下一例:
class B{
public:
int m_iNum;
}
void foo(){
const B b1;
b1.m_iNum = 100; //comile error
B b2 = const_cast<B>(b1);
b2. m_iNum = 200; //fine
}
上面的代码编译时会报错,因为b1是一个常量对象,不能对它进行改变;
使用const_cast把它转换成一个常量对象,就可以对它的数据成员任意改变。注意:b1和b2是两个不同的对象。
== dynamic_cast .vs. static_cast
class B { ... };
class D : public B { ... };
void f(B* pb)
{
D* pd1 = dynamic_cast<D*>(pb);
D* pd2 = static_cast<D*>(pb);
}
If pb really points to an object of type D, then pd1 and pd2 will get the same value. They will also get the same value if pb == 0.
If pb points to an object of type B and not to the complete D class, then dynamic_cast will know enough to return zero. However, static_cast relies on the programmer’s assertion that pb points to an object of type D and simply returns a pointer to that supposed D object.
即dynamic_cast可用于继承体系中的向下转型,即将基类指针转换为派生类指针,比static_cast更严格更安全。dynamic_cast在执行效率上比static_cast要差一些,但static_cast在更宽上范围内可以完成映射,这种不加限制的映射伴随着不安全性。static_cast覆盖的变换类型除类层次的静态导航以外,还包括无映射变换、窄化变换(这种变换会导致对象切片,丢失信息)、用VOID*的强制变换、隐式类型变换等...
== static_cast .vs. reinterpret_cast
reinterpret_cast是为了映射到一个完全不同类型的意思,这个关键词在我们需要把类型映射回原有类型时用到它。我们映射到的类型仅仅是为了故弄玄虚和其他目的,这是所有映射中最危险的。(这句话是C++编程思想中的原话)
static_cast 和 reinterpret_cast 操作符修改了操作数类型。它们不是互逆的; static_cast 在编译时使用类型信息执行转换,在转换执行必要的检测(诸如指针越界计算, 类型检查). 其操作数相对是安全的。另一方面;reinterpret_cast 仅仅是重新解释了给出的对象的比特模型而没有进行二进制转换, 例子如下:
int n=9; double d=static_cast < double > (n);
上面的例子中, 我们将一个变量从 int 转换到 double。 这些类型的二进制表达式是不同的。 要将整数 9 转换到 双精度整数 9,static_cast 需要正确地为双精度整数 d 补足比特位。其结果为 9.0。而reinterpret_cast 的行为却不同:
int n=9;
double d=reinterpret_cast<double & > (n);
这次, 结果有所不同. 在进行计算以后, d 包含无用值. 这是因为 reinterpret_cast 仅仅是复制 n 的比特位到 d, 没有进行必要的分析.
因此, 你需要谨慎使用 reinterpret_cast.
标准c++中主要四种强制转换类型运算符
标准c++中主要有四种强制转换类型运算符:
const_cast,reinterpret_cast,static_cast,dynamic_cast等等。
1、static_cast 是静态转换,只能用于类型兼容的转换(由宽向窄), 无RUNTIME损耗 可以用来对多态对象的指针进行高效的downcasting,但是当被转换的指针实际上指向错误的派生类时,它没有提供错误检测
static_cast(expression)将expression转换到独立地基于表达式中给出的类型type_id类型。没有运行类型检查被执行以确保转换的安全性。static_cast运算符可被用在例如转换一个基类指针到一个派生类指针的操作,这样的转换不总是安全的,相对而言,dynamic_cast是安全的。
(注意:static_cast<>()转换只是返回当前强制类型转换过后的值,也就是说,使用该操作符进行强制类型转换的时候,并不改变用于转换的源值,而只是暂时借用源值转换并保存值,所以要定义另外一个变量来保存转换以后的值。)
例:
class B {... };
class D :public B { ... };
void f(B*pb, D* pd)
{
D* pd2 = static_cast<D*>(pb); // 不安全, pb可能只是B的指针
B* pb2 = static_cast<B*>(pd); // 安全的
...
}
2、 dynamic_cast 是动态转换,主要用于base向derived的转换,就是说对象本身包含了自己
的type information,这个type information在转换过程中不会损失
例:
class A {... };
class B {... };
void f()
{
A* pa = new A;
B* pb = new B;
void* pv = dynamic_cast<void*>(pa);
// pv 现在指向了一个类型为A的对象
...
pv = dynamic_cast<void*>(pb);
// pv 现在指向了一个类型为B的对象
}
3、reinterpret_cast运算符允许任何指针被转换到任何其它指针类型,它还允许
任何整型转换到任意指针类型,且反之亦然。滥用reinterpret_cast运算符可轻易导致不安全,除非是所期望的转换是固有的低等级,否则你应使用其它转换运算符之一。
例:
class A {... };
class B {... };
void f()
{
A* pa = new A;
void* pv = reinterpret_cast<B*>(pa);
// pv 现在指向了一个类型为B的对象,这可能是不安全的
...
}
4、const_cast运算符可被用于从一个类中除去const、volatile和_ _unaligned属性
任何对象类型的指针或一个数据成员的指针可被显式地转换到完全相同的类型,带const,volatile和__unaligned限定符除外。对指针和引用,结果将指向源对象,对数据成员指针结果和数据成员的源指针一样指向同一成员。由于依赖于引用对象的类型,通过指针、引用或数据成员指针的求结果的写操作将产生未定义的动作
例:
class A {... };
void f()
{
const A*pa = new A;//const对象
A *pb;//非const对象
//pb =pa; // 这里将出错,不能将const对象指针赋值给非const对象
pb =const_cast<A*>(pa); // 现在OK了
强制转换的发展:
C 时代 (int)value;
C++ 前期 int(value);
C++ 标准时期
1 dynamic_cast
2 const_cast
3 static_cast
4 reinterpret_cast
C++ 标准的用法
dynamic_cast<T>(a):T,a都是指针,主要用于类与类之间的转换,T必须是a的基类,否则返回NULL,安全性较高
const_cast<T>(a):T,a必须是相同的类型,在a具有const定义时用于去除const约束
static_cast<T>(a):T,a可以是指针,枚举或是基本类型,一般用于基本类型及其指针(比如 int*)的转换,安全性较dynamic_cast低,类之间的转换推荐使用dynamic_cast
reinterpret_cast<T>(a):乱来..安全性最低,但是最灵活,在已确保指针安全的情况下想怎么转就怎么转
C语言中强制类型转换:
字符型变量的值实质上是一个8位的整数值,因此取值范围一般是-128~127,char型变量也可以加修饰符unsigned,则unsigned char 型变量的取值范围是0~255(有些机器把char型当做unsighed char型对待, 取值范围总是0~255)。
● 如果一个运算符两边的运算数类型不同,先要将其转换为相同的类型,即较低类型转换为较高类型,然后再参加运算,转换规则如下图所示。
double ←── float 高
↑
long
↑
unsigned
↑
int ←── char,short 低
● 图中横向箭头表示必须的转换,如两个float型数参加运算,虽然它们类型相同,但仍要先转成double型再进行运算,结果亦为double型。 纵向箭头表示当运算符两边的运算数为不同类型时的转换,如一个long 型数据与一个int型数据一起运算,需要先将int型数据转换为long型, 然后两者再进行运算,结果为long型。所有这些转换都是由系统自动进行的, 使用时你只需从中了解结果的类型即可。这些转换可以说是自动的,但然,C语言也提供了以显式的形式强制转换类型的机制。
● 当较低类型的数据转换为较高类型时,一般只是形式上有所改变, 而不影响数据的实质内容, 而较高类型的数据转换为较低类型时则可能有些数据丢失。
当赋值运算符两边的运算对象类型不同时,将要发生类型转换, 转换的规则是:把赋值运算符右侧表达式的类型转换为左侧变量的类型。具体的转换如下:
(1) 浮点型与整型
● 将浮点数(单双精度)转换为整数时,将舍弃浮点数的小数部分, 只保留整数部分。
将整型值赋给浮点型变量,数值不变,只将形式改为浮点形式, 即小数点后带若干个0。注意:赋值时的类型转换实际上是强制的。
(2) 单、双精度浮点型
● 由于C语言中的浮点值总是用双精度表示的,所以float 型数据只是在尾部加0延长为doub1e型数据参加运算,然后直接赋值。doub1e型数据转换为float型时,通过截尾数来实现,截断前要进行四舍五入操作。
(3) char型与int型
● int型数值赋给char型变量时,只保留其最低8位,高位部分舍弃。
● chr型数值赋给int型变量时, 一些编译程序不管其值大小都作正数处理,而另一些编译程序在转换时,若char型数据值大于127,就作为负数处理。对于使用者来讲,如果原来char型数据取正值,转换后仍为正值;如果原来char型值可正可负,则转换后也仍然保持原值, 只是数据的内部表示形式有所不同。
(4) int型与1ong型
● long型数据赋给int型变量时,将低16位值送给int型变量,而将高16 位截断舍弃。(这里假定int型占两个字节)。
将int型数据送给long型变量时,其外部值保持不变,而内部形式有所改变。
(5) 无符号整数
● 将一个unsigned型数据赋给一个占据同样长度存储单元的整型变量时(如:unsigned→int、unsignedlong→long,unsignedshort→short) ,原值照赋,内部的存储方式不变,但外部值却可能改变。
● 将一个非unsigned整型数据赋给长度相同的unsigned型变量时, 内部存储形式不变,但外部表示时总是无符号的。
/*例:赋值运算符举例 */
main()
{ unsigned a,b;
int i,j;
a=65535;
i=-1;
j=a;
b=i;
printf("(unsigned)%u→(int)%d\n",a,j);
printf("(int)%d→(unsigned)%u\n",i,b);
}
运行结果为:
(unsigned)65535→(int)-1
(int)-1→(unsigned)65535
● 计算机中数据用补码表示,int型量最高位是符号位,为1时表示负值,为0时表示正值。如果一个无符号数的值小于32768则最高位为0,赋给 int型变量后、得到正值。如果无符号数大于等于32768,则最高位为1,赋给整型变量后就得到一个负整数值。反之,当一个负整数赋给unsigned型变量时,得到的无符号值是一个大于32768的值。
● C语言这种赋值时的类型转换形式可能会使人感到不精密和不严格,因为不管表达式的值怎样,系统都自动将其转为赋值运算符左部变量的类型。
● 而转变后数据可能有所不同,在不加注意时就可能带来错误。这确实是个缺点,也遭到许多人们批评。但不应忘记的是:c面言最初是为了替代汇编语言而设计的,所以类型变换比较随意。当然,用强制类型转换是一个好习惯,这样,至少从程序上可以看出想干什么。
标准c++中主要有四种强制转换类型运算符
const_cast,reinterpret_cast,static_cast,dynamic_cast等等。
1)static_cast<T*>(a)
将地址a转换成类型T,T和a必须是指针、引用、算术类型或枚举类型。
表达式static_cast<T*>(a), a的值转换为模板中指定的类型T.在运行时转换过程中,不进行类型检查来确保转换的安全性。
例子:
class B { ... }; class D : public B { ... }; void f(B* pb, D* pd) { D* pd2 = static_cast<D*>(pb); //不安全, pb可能只是B的指针
... } class D : public B { ... }; void f(B* pb, D* pd) { D* pd2 = static_cast<D*>(pb); //不安全, pb可能只是B的指针
... }
class B { ... }; class D : public B { ... }; void f(B* pb, D* pd) { D* pd2 = static_cast<D*>(pb); //不安全, pb可能只是B的指针
... } |
2)dynamic_cast<T*>(a)
完成类层次结构中的提升。T必须是一个指针、引用或无类型的指针。a必须是决定一个指针或引用的表达式。
表达式dynamic_cast<T*>(a)将a值转换为类型为T的对象指针。如果类型T不是a的某个基类型,该操作将返回一个空指针。
例子:
class A { ... }; class B { ... }; void f() { A* pa = new A; B* pb = new B; void* pv = dynamic_cast<A*>(pa); // pv 现在指向了一个类型为A的对象 ... pv = dynamic_cast<B*>(pb); // pv 现在指向了一个类型为B的对象 } |
3)const_cast<T*>(a)
去掉类型中的常量,除了const或不稳定的变址数,T和a必须是相同的类型。
表达式const_cast<T*>(a)被用于从一个类中去除以下这些属性:const, volatile,和 __unaligned.
例子:
class A { ... }; void f() { const A *pa = new A;//const对象 A *pb;//非const对象 //pb = pa; // 这里将出错,不能将const对象指针赋值给非const对象 pb = const_cast<A*>(pa); // 现在OK了 } class A { ... }; void f() { const A *pa = new A;//const对象 A *pb;//非const对象 //pb = pa; // 这里将出错,不能将const对象指针赋值给非const对象 pb = const_cast<A*>(pa); // 现在OK了 }
class A { ... }; void f() { const A *pa = new A;//const对象 A *pb;//非const对象
pb = const_cast<A*>(pa); // 现在OK了 } |
4)reinterpret_cast<T*>(a)
任何指针都可以转换成其它类型的指针,T必须是一个指针、引用、算术类型、指向函数的指针或指向一个类成员的指针。
表达式reinterpret_cast<T*>(a)能够用于诸如char*到 int*,或者One_class*到Unrelated_class*等类似这样的转换,因此可能是不安全的。
例子:
class A { ... }; class B { ... }; void f() { A* pa = new A; void* pv = reinterpret_cast<A*>(pa); // pv 现在指向了一个类型为B的对象,这可能是不安全的 ... } |
强制类型转换包括以下名字命名的强制类型转换操作符:static_cast dynamic_cast const_cast reinterpret_cast 。
1、何时需要强制类型转换
因为要覆盖通常的标准转换,所以需显示使用强制类型转换。下面的复合赋值:
double dval;
int ival;
ival *= dval; 为了与dval做乘法操作,需将ival转换为double型,然后将乘法操作的double型结果截尾为int型,在赋值给ival。为了去掉将 ival转换为double型这个不必要的转换,可通过如下强制将dval转换为int型:
ival *= static_cast<int>(dval);
2、命名的强制类型转换命名的强制类型转换符号的一般形式如下:
cast-name<type>(expression);其中cast-name为static_cast dynamic_cast const_cast reinterpret_cast 之一,type为转换的目标类型,而expression则是被类型强制转换的值。
3、dynamic_cast 支持运行时识别指针或引用所指向的对象。多用于多态情况.主要用于执行“安全的向下转型(safe downcasting)”,也就是说,要确定一个对象是否是一个继承体系中的一个特定类型。它是唯一不能用旧风格语法执行的强制转型,也是唯一可能有重大运行时代价的强制转型
4、const_cast将转换掉表达式的const性质,例如,假设有函数string_copy,只有唯一的参数,为char*类型,我们对访 问函数时,如让它接受const char *类型的参数,可通过const_cast用一个const值调用string_copy函数:
const char * pc_str;
char * pc = string_copy(const_cast<char *>(pc_str));
只有使用const_cast才能将const性质转换掉。其他三种不行,会导致编译时错误。
5、static_cast 编译器隐式执行的任何类型转换都可以由static_cast显式转换。
6、reinterpret_cast 通常为操作数的位模式提供较低层次的重新解释。
例如: 对于下面的强制转换:
int * ip;
char * pc = reinterpret_cast<char *>(ip);
程序员必须永远记得pc所指向的真实对象其实是int型,而并非字符数组。任何假设pc是普通字符指针的应用都有可能带来运行时错误。
·dynamic_cast 主要用于执行“安全的向下转型(safe downcasting)”,也就是说,要确定一个对象是否是一个继承体系中的一个特定类型。它是唯一不能用旧风格语法执行的强制转型,也是唯一可能有重大运行时代价的强制转型
·static_cast 可以被用于强制隐型转换(例如,non-const 对象转型为 const 对象,int 转型为 double,等等),它还可以用于很多这样的转换的反向转换(例如,void* 指针转型为有类型指针,基类指针转型为派生类指针),但是它不能将一个 const 对象转型为 non-const 对象(只有 const_cast 能做到),它最接近于C-style的转换。
·const_cast 一般用于强制消除对象的常量性。它是唯一能做到这一点的 C++ 风格的强制转型。
·reinterpret_cast 是特意用于底层的强制转型,导致实现依赖(implementation-dependent)(就是说,不可移植)的结果,例如,将一个指针转型为一个整数。这样的强制转型在底层代码以外应该极为罕见。