标准C++强制类型转换(C++风格的类型转换)

来源:互联网 发布:大型企业网络搭建 编辑:程序博客网 时间:2024/06/06 17:25
 (1)
1static_cast< T >(a)
将地址a转换成类型TTa必须是指针、引用、算术类型或枚举类型。
表达式static_cast< T > ( a ) a的值转换为模板中指定的类型T。在运行时转换过程中,不进行类型检查来确保转换的安全性。
2dynamic_cast< T >(a)
完成类层次结构中的提升。T必须是一个指针、引用或无类型的指针。a必须是决定一个指针或引用的表达式。
表达式dynamic_cast< T >( a ) a值转换为类型为T的对象指针。如果类型T不是a的某个基类型,该操作将返回一个空指针。(为了支援执行时期的型态转换动作,C++提供了dynamic_cast用来将一个基底类别的指针转型至衍生类别指针,称之为「安全向下转型」(Safe downcasting),它在执行时期进行型态转换动作,首先会确定转换目标与来源是否属同一个类别阶层,接着才真正进行转换的动作,检验动作在执行时期完成,如果是一个指针,则转换成功时传回位址,失败的话会传回0,如果是参考的话,转换失败会丢出bad_cast例外)
3const_cast(a)
去掉类型中的常量,除了const或不稳定的变址数,Ta必须是相同的类型。
表达式const_cast< T >( a )被用于从一个类中去除以下这些属性:const, volatile, __unaligned
4reinterpret_cast(a)
任何指针都可以转换成其它类型的指针,T必须是一个指针、引用、算术类型、指向函数的指针或指向一个类成员的指针。
表达式reinterpret_cast< T >( a )能够用于诸如char* int*,或者One_class* Unrelated_class*等类似这样的转换,因此可能是不安全的。


标准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的指针

    B* pb2 = static_cast<B*>(pd);        // 安全的
    ...
}

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);        // 安全的
    ...
}

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<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<void*>(pa);
   // pv 现在指向了一个类型为A的对象
   ...
   pv = dynamic_cast<void*>(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 = pa; // 这里将出错,不能将const对象指针赋值给非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<B*>(pa);
   // pv 现在指向了一个类型为B的对象,这可能是不安全的
   ...
}

C++风格的类型转换的用法
这是More Effecitve C++里的第二条对类型转换讲的很好,也很基础好懂。
Item M2:尽量使用C++风格的类型转换
仔细想想地位卑贱的类型转换功能(cast),其在程序设计中的地位就象goto语句一样令人鄙视。但是它还不是无法令人忍受,因为当在某些紧要的关头,类型转换还是必需的,这时它是一个必需品。
不过C风格的类型转换并不代表所有的类型转换功能。
一来它们过于粗鲁,能允许你在任何类型之间进行转换。不过如果要进行更精确的类型转换,这会是一个优点。在这些类型转换中存在着巨大的不同,例如把一个指向 const对象的指针(pointer-to-const-object)转换成指向非const对象的指针(pointer-to-non-const -object)(即一个仅仅去除const的类型转换),把一个指向基类的指针转换成指向子类的指针(即完全改变对象类型)。传统的C风格的类型转换不对上述两种转换进行区分。(这一点也不令人惊讶,因为C风格的类型转换是为C语言设计的,而不是为C++语言设计的)。
二来C风格的类型转换在程序语句中难以识别。在语法上,类型转换由圆括号和标识符组成,而这些可以用在C++中的任何地方。这使得回答象这样一个最基本的有关类型转换的问题变得很困难:“在这个程序中是否使用了类型转换?”。这是因为人工阅读很可能忽略了类型转换的语句,而利用象grep的工具程序也不能从语句构成上区分出它们来。
C++通过引进四个新的类型转换操作符克服了C风格类型转换的缺点,这四个操作符是, static_cast, const_cast, dynamic_cast, 和reinterpret_cast。在大多数情况下,对于这些操作符你只需要知道原来你习惯于这样写,
(type) expression
而现在你总应该这样写:
static_cast
<type>(expression)
例如,假设你想把一个int转换成double,以便让包含int类型变量的表达式产生出浮点数值的结果。如果用C风格的类型转换,你能这样写:
int firstNumber, secondNumber;

double result = ((double)firstNumber)/secondNumber;
如果用上述新的类型转换方法,你应该这样写:
double result = static_cast
<double>(firstNumber)/secondNumber;
这样的类型转换不论是对人工还是对程序都很容易识别。
static_cast 在功能上基本上与C风格的类型转换一样强大,含义也一样。它也有功能上限制。例如,你不能用static_cast象用C风格的类型转换一样把 struct转换成int类型或者把double类型转换成指针类型,另外,static_cast不能从表达式中去除const属性,因为另一个新的类型转换操作符const_cast有这样的功能。
其它新的C++类型转换操作符被用在需要更多限制的地方。

const_cast用于类型转换掉表达式的const或volatileness属性。通过使用const_cast,你向人们和编译器强调你通过类型转换想做的只是改变一些东西的 constness或者volatileness属性。这个含义被编译器所约束。如果你试图使用const_cast来完成修改constness 或者volatileness属性之外的事情,你的类型转换将被拒绝。下面是一些例子:

class Widget {  ...};
class SpecialWidget: public Widget { ....};
void update(SpecialWidget *psw);
SpecialWidget sw; // sw 是一个非const 对象。
const SpecialWidget& csw = sw; // csw 是sw的一个引用
// 它是一个const 对象
update(
&csw); // 错误!不能传递一个const SpecialWidget* 变量
// 给一个处理SpecialWidget*类型变量的函数
update(const_cast
<SpecialWidget*>(&csw));
// 正确,csw的const被显示地转换掉( csw和sw两个变量值在update函数中能被更新)
update((SpecialWidget*)
&csw);

// 同上,但用了一个更难识别的C风格的类型转换
Widget *pw = new SpecialWidget;
update(pw); // 错误!pw的类型是Widget*,但是
// update函数处理的是SpecialWidget*类型
update(const_cast
<SpecialWidget*>(pw));
// 错误!const_cast仅能被用在影响constness or volatileness的地方上。
// 不能用在向继承子类进行类型转换
到目前为止,const_cast最普通的用途就是转换掉对象的const属性。

第二种特殊的类型转换符是dynamic_cast,它被用于安全地沿着类的继承关系向下进行类型转换。这就是说,你能用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更安全。

 

例子::

 Widget *pw;

update(dynamic_cast<SpecialWidget*>(pw));
// 正确,传递给update函数一个指针是指向变量类型为SpecialWidget的pw的指针
// 如果pw确实指向一个对象,否则传递过去的将使空指针。
void updateViaRef(SpecialWidget& rsw);
updateViaRef(dynamic_cast
<SpecialWidget&>
(*pw));
//正确。传递给updateViaRef函数SpecialWidget pw 指针,如果pw确实指向了某个对象
// 否则将抛出异常
dynamic_casts在帮助你浏览继承层次上是有限制的。它不能被用于缺乏虚函数的类型上(参见条款M24),也不能用它来转换掉constness:
int firstNumber, secondNumber;

double result = dynamic_cast
<double>
(firstNumber)/secondNumber;
// 错误!没有继承关系
const SpecialWidget sw;

update(dynamic_cast
<SpecialWidget*>(&sw
));
// 错误! dynamic_cast不能转换掉const。

如你想在没有继承关系的类型中进行转换,你可能想到static_cast。如果是为了去除const,你总得用const_cast。


这四个类型转换符中的最后一个是reinterpret_cast。使用这个操作符的类型转换,其的转换结果几乎都是执行期定义(implementation-defined)。因此,使用reinterpret_casts的代码很难移植。
reinterpret_casts的最普通的用途就是在函数指针类型之间进行转换。例如,假设你有一个函数指针数组:
typedef void (*FuncPtr)(); // FuncPtr is 一个指向函数的指针,该函数没有参数 返回值类型为void
FuncPtr funcPtrArray[10]; // funcPtrArray 是一个能容纳
// 10个FuncPtrs指针的数组
让我们假设你希望(因为某些莫名其妙的原因)把一个指向下面函数的指针存入funcPtrArray数组:
int doSomething();
你不能不经过类型转换而直接去做,因为doSomething函数对于funcPtrArray数组来说有一个错误的类型。在FuncPtrArray数组里的函数返回值是void类型,而doSomething函数返回值是int类型。
funcPtrArray[0] = 
&doSomething; // 错误!类型不匹配
reinterpret_cast可以让你迫使编译器以你的方法去看待它们:
funcPtrArray[0]   =                                       //   this   compiles
    reinterpret_cast <FuncPtr> (&doSomething);
转换函数指针的代码是不可移植的(C++不保证所有的函数指针都被用一样的方法表示),在一些情况下这样的转换会产生不正确的结果(参见条款M31),所以你应该避免转换函数指针类型,除非你处于着背水一战和尖刀架喉的危急时刻。

(2)

对于从子类到基类的指针转换,static_cast和dynamic_cast都是成功并且正确的(所谓成功是说转换没有编译错误或者运行异常;所谓正确是指方法的调用和数据的访问输出是期望的结果),这是面向对象多态性的完美体现。

而从基类到子类的转换,static_cast和dynamic_cast都是成功的,但是正确性方面,我对两者的结果都先进行了是否非空的判别:dynamic_cast的结果显示是空指针,而static_cast则是非空指针。但很显然,static_cast的结果应该算是错误的,子类指针实际所指的是基类的对象,而基类对象并不具有子类的Study()方法(除非妈妈又想去接受个"继续教育")。

对于没有关系的两个类之间的转换,输出结果表明,dynamic_cast依然是返回一个空指针以表示转换是不成立的;static_cast直接在编译期就拒绝了这种转换。

reinterpret_cast成功进行了转换,而且返回的值并不是空指针,但是结果显然是错误的,因为Children类显然不具有Stranger的Self_Introduce()。虽然两者都具有name数据成员和Speak()方法,,Speak()方法也只是调用了该相同名称的成员而已,但是对于Speak()的调用直接造成了程序的崩溃。

其实前面static_cast的转换的结果也会跟reinterpret_cast一样造成的程序的崩溃,只是类的方法都只有一份,只有数据成员属于对象,所以在调用那些不会访问对象的数据的方法时(如Stranger的Self_Introduce())并不会造成崩溃。而daughter_s->Speak();和daughter_s->Study();调用了数据成员却没有出现运行错误,则是因为该成员是从基类继承下来的,通过地址偏移可以正确的到达数据成员所在的地址以读取出数据。

最后,程序里还用dynamic_cast希望把用其他转换运算符转换过去的指针转换回来。对于使用static_cast转换后指向了子类对象的基类指针,dynamic_cast判定转换是合理有效的,因此转换成功获得一个非空的指针并且正确输出了结果;而对于reinterpret_cast转换的类型,的确如它的功能一样——重新解析,变成新的类型,所以才得到dynamic_cast判定该类型已经不是原来的类型结果,转换得到了一个空指针。

总得说来,static_cast和reinterpret_cast运算符要么直接被编译器拒绝进行转换,要么就一定会得到相应的目标类型的值。 而dynamic_cast却会进行判别,确定源指针所指的内容,是否真的合适被目标指针接受。如果是否定的,那么dynamic_cast则会返回null。这是通过检查"运行期类型信息"(Runtime type information,RTTI)来判定的,它还受到编译器的影响,有些编译器需要设置开启才能让程序正确运行(导师的PPT详细介绍了Visual Studio的情况),因此dynamic_cast也就不能用传统的转换方式来实现了。

虚函数(virtual function)对dynamic_cast的作用
已经在前面反复提到过面向对象的多态性,但是这个多态性到底要如何体现呢?dynamic_cast真的允许任意对象指针之间进行转换,只是最后返回个null值来告知转换无结果吗?

实际上,这一切都是虚函数(virtual function)在起作用。

在C++的面对对象思想中,虚函数起到了很关键的作用,当一个类中拥有至少一个虚函数,那么编译器就会构建出一个虚函数表(virtual method table)来指示这些函数的地址,假如继承该类的子类定义并实现了一个同名并具有同样函数签名(function siguature)的方法重写了基类中的方法,那么虚函数表会将该函数指向新的地址。此时多态性就体现出来了:当我们将基类的指针或引用指向子类的对象的时候,调用方法时,就会顺着虚函数表找到对应子类的方法而非基类的方法。

当然虚函数表的存在对于效率上会有一定的影响,首先构建虚函数表需要时间,根据虚函数表寻到到函数也需要时间。

因为这个原因如果没有继承的需要,一般不必在类中定义虚函数。但是对于继承来说,虚函数就变得很重要了,这不仅仅是实现多态性的一个重要标志,同时也是dynamic_cast转换能够进行的前提条件。

假如去掉上个例子中Stranger类析构函数前的virtual,那么语句

Children* child_r = dynamic_cast<Children*> (stranger_r);

在编译期就会直接报出错误,具体原因不是很清楚,我猜测可能是因为当类没有虚函数表的时候,dynamic_cast就不能用RTTI来确定类的具体类型,于是就直接不通过编译。

这不仅仅是没有继承关系的类之间的情况,如果基类或者子类没有任何虚函数(如果基类有虚函数表,子类当然是自动继承了该表),当他们作为dynamic_cast的源类型进行转换时,编译也会失败。

这种情况是有可能存在的,因为在设计的时候,我们可能不需要让子类重写任何基类的方法。但实际上,这是不合理的。导师在讲解多态性的时候,时刻强调了一点:如果要用继承,那么一定要让析构函数是虚函数;如果一个函数是虚函数,那么在子类中也要是虚函数。

我会将导师关于"为何继承中析构函数必须是虚函数"的讲解总结一下,当然你也可以看这边文章来了解原因。

(3)
(1)dynamic_cast主要的作用是有多态性的基类指针或者引用转为为派生类指针或者引用.

(2)dynamic_cast用于有派生关系类的转换
Parents* p=new children();
children* c=dynamic_cast<chileren*>(p);
只有 p指向的确实是children对象才会成功.

(3)

1.static_cast只当c语言中的强制转换()用.转化一些常规类型
2.dynamic_cast只对有派生关系的类使用,一般用在基类到子类的转化!
请看你的例子:
Parents * mother_d = dynamic_cast<Parents*> (daughter_d);
Parents * father_s = static_cast<Parents*> (son_s);

这是子类到基类,不用转化!所以你的这两个都能成功,如果非得要用的化Parents * mother_d = dynamic_cast<Parents*> (daughter_d);这个应该是更合理的(其实都合理)

按照编译器的视野,Parents *指向的范围要窄,children* 要宽.编译器应该用隐式转换,更接近于static_cast<>

(4)

对于在没有继承关系的类型上使用,对比也可以看出一些问题:
继承不代表有多态性,继承只是多态性的一个前提条件。
dynamic_cast需要具有多态性的类才能进行转换。virtual function是保证多态性的条件。

如果没有virtual,即使是有继承关系的类型之间,也不能使用dynamic_cast进行转换。
VS报出的编译错误很直接的:type is not polymorphic  (当类没有任何虚/继承时 则表示它只是它自己的类型而不能和任何其他类型转换 所以dynamic_cast直接报编译错误了吧)

 

//*****************************网友理解*******************************************

  dynamic_cast:动态类型转换  
  static_cast:静态类型转换  
  reinterpret_cast:重新解释类型转换  
  const_cast:常量类型转换  
  专业的上面很多了,我说说我自己的理解吧:   
  dynamic_cast一般用在父类和子类指针应用的互相转化;  
  static_cast一般是普通数据类型(如int   m=static_cast<int>(3.14));  
  reinterpret_cast很像c的一般类型转换操作  
  const_cast是把const或volatile属性去掉

//*****************************网友理解*******************************************

static_cast是最普通的转换,比如:int转换成double。   
const_cast最普通的用途就是转换掉对象的const属性,详见第21条款。   
dynamic_cast把指向基类的指针或引用转换成其派生类或兄弟类的指针引用(Effective C++第39条款)

reinterpret_cast最普通的用途就是在函数类型指针之间进行转换。  

 

原创粉丝点击