关于C/C++中的类型转换资料集锦

本文详细介绍了C语言中的类型强制转换及其注意事项,并对比了C++中的四种类型转换(dynamic_cast、static_cast、reinterpret_cast、const_cast)的用法与特点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

<<<<<<<<<<<<<<C语言类型强制转换>>>>>>>>>>>>>>

http://baike.baidu.com/link?url=Y3QGwE3bWOY3VfAHXvB4JYVtLM8mhM060DQrPdeVX__ISZyCzjO1pCDI9H5RCOdIcPmUYeeV_-SgzTYntsRaXa

C语言类型强制转换

  编辑
强制类型转换是通过类型转换 运算来实现的。其一般形式为: (类型说明符) ( 表达式) 其功能是把表达式的运算结果 强制转换成类型说明符所表示的类型。
中文名
C语言类型强制转换
外文名
C语言类型强制转换
形式为
 ( 表达式
通    过
类型转换运算来实现的

目录

基本介绍

编辑
强制类型转换是通过类型转换运算来实现的。其一般形式为:(类型说明符)( 表达式)其功能是把表达式的运算结果 强制转换成类型说明符所表示的类型。自动转换是在源类型和目标类型兼容以及目标类型广于源类型时发生一个类型到另一类的转换。例如: (float) a 把a转换为浮点型,(int)(x+y) 把x+y的结果转换为 整型。在使用强制转换时应注意以下问题:

注意事项

编辑
1.类型说明符和 表达式都必须加括号(单个变量可以不加括号),如把(int)(x+y)写成(int)x+y则成了把x转换成int型之后再与y相加了。
2.无论是 强制转换或是自动转换,都只是为了本次运算的需要而对变量的数据长度进行的临时性转换,而不改变数据说明时对该变量定义的类型。
例1:
main()
{
float f=5.75;
printf("f=%d,f=%f\n",(int)f,f);
}
f=5,f=5.750000
将float f 强制转换成int f float f=5.75;printf("(int)f=%d,f=%f\n",(int)f,f); 本例表明,f虽强制转为int型,但只在运算中起作用, 是临时的,而f本身的类型并不改变。因此,(int)f的值为 5(删去了小数)而f的值仍为5.75。
例2:
比如我们可以(int)'A',这样转换后的结果为A的ASCII码数值,因为那块内存本来就存的那个数,只是换个形式使用而已。 知道上面的原则,我们可以对任何数据类型进行转换,但是转换的结果可能不是你想像的结果,举例(int)'9'的结果为多少?不是9而是0x39。来个高深点的printf("%d",‘12’);的输出是什么?正确答案是12594,因为printf("%d",'12'),打印的是存储12的 内存地址上的内容,即ASCII码值2存储在低位,1储在高位地址,0x32就是2的ASCII码,0x31就是1的ASCII码,所以是0x3132,转换成10进制就是12594!
●  字符型变量的值实质上是一个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延长为double型数据参加运算,然后直接赋值。double型数据转换为float型时,通过截尾数来实现,截断前要进行四舍五入操作。
(3) char型与int型
● int型数值赋给char型变量时,只保留其最低8位,高位部分舍弃。
● char型数值赋给int型变量时, 一些 编译程序不管其值大小都作正数处理,而另一些编译程序在转换时,若char型数据值大于127,就作为负数处理。对于使用者来讲,如果原来char型数据取正值,转换后仍为正值;如果原来char型值可正可负,则转换后也仍然保持原值, 只是数据的内部表示形式有所不同。
(4) int型与long型
● long型数据赋给int型变量时,将低16位值送给int型变量,而将高16 位截断舍弃。(这里假定int型占两个字节)。 将int型数据送给long型变量时,其外部值保持不变,而内部形式有所改变。
● 将一个unsigned型数据赋给一个占据同样长度 存储单元整型变量时(如:unsigned→int、unsigned long→long,unsigned short→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++类型转换使用总结>>>>>>>>>>>>>>


http://jingyan.baidu.com/article/6181c3e08f0a62152ef153f6.html

C++类型转换使用总结
听语音

  • 浏览:319
  • |
  • 更新:2015-04-21 21:15

       对于C语言的松散类型转换,Stroustrop采取的措施是更严格地限制允许的类型转换,并添加4个类型转换运算符,使转换过程更规范:

dynamic_cast;

const_cast;

static_cast;

reinterpret_cast;

       可以根据目的选择一个更适合的运算符,而不是使用通用的类型转换。这指出了进行类型转换的原因,并让编译器能够检查程序的行为是否与设计者想法吻合。

       使用这四种类型转换运算符不需要导入其它的头文件,以及任何namespace。因为他们是运算符,地位和sizeof( )、+ 、/ 等一致。

dynamic_cast 类型转换运算符

  1. 1

          dynamic_cast运算符的用途是使得能够在类层次结构中进行向上转换(属于is-a关系,这样的类型转换是安全的),而不允许其它类型转换。

    该运算符的语法如下:

    dynamic_cast <type-name> (experssion)    //注意必须有这对小括号,下同

  2. 2

          dynamic_cast运算符已经在前面介绍过了。总之,假设High和Low是两个类,而ph和pl类型分别为High *和Low *,则仅当Low是High的可访问基类(直接或间接)时,下面的语句才将一个High *指针赋给(转换成Low *)pl:

    pl = dynamic_cast<Low *> ph;

          否则,该语句将空指针赋给pl,即pl = NULL或pl = 0。

    END

const_cast 类型转换运算符

  1. 1

           const_cast运算符用于执行只有一种用途的类型转换,即改变为有或无const或volatile(注:volatile adj.易变的)特征,其语法为:

    const_cast <type_name> (expression)

           如果类型的其他方面也被修改,则上述类型转换将出错。也就是说,除了const或volatile特征(有或无)可以不同外,type_name和expression的类型(指数据类型)必须相同。

  2. 2

           const_cast只能去除指针上的const属性,而不能去除已经定义好的变量上的const属性(如,const int counter),因为变量的存储类型一旦定义后就能不能改变。因此对除指针、数据成员指针、引用外的const类型变量使用const_cast类型转换符是非法的。

    const int counter = 0;

    int num = 12;

    num = const_cast<int> (counter);   //invalid  #1

    //-------------------  convert 'const int *' to 'int *'  -------------------//

    const int * pInt = &num;

    int * iptr = const_cast<int *> (pInt);   //valid   #2

    //------------------- convert 'int * const' to 'int *'-------------------//

    int * const icptr = &num;

    iptr = const_cast<int *> (icptr);   //valid   #3

    //------------ convert 'const int * const' to 'const int *' ---------//

    const int * const cicptr = &num;

    pInt = const_cast<const int *> (cicptr);   //valid   #4

    //---------------- convert 'int * const' to 'int *' ---------------//

    int * const icptr = &num;

    iptr = const_cast<int *> (icptr);   //valid   #5

    //----------- convert 'const int * const' to 'int *' ---------//

    iptr = const_cast<int *> (cicptr);     //valid   #6

    //-----------  #1编译错误提示

          在类型‘int’上使用 const_cast 无效,因为它既不是指针,也不是引用,也不是数据成员指针。

  3. 3

    假设High和Low是两个类:

    High bar;

    const High *pbar = &bar;

    ……

    High * pb = const_cast<High *>(pbar);   //valid

    const Low *pl = const_cast<const Low *>(pbar);   //invalid

            第一个类型转换使得*pb成为一个可用于修改bar对象值的指针,它删除const标签。第二个类型转换是非法的,因为他同时尝试将类型从const High * 改为const Low *。

            提供该运算符的原因是有时候可能需要这样的一个值,它在大多数情况下是常量,而有时候又是可以修改的。在这种情况下,可以将这个值声明为const,并在需要的时候修改它的时候使用const_cast。

  4. 4

    补充:volatile 关键字

    作用:volatile 屏蔽系统对变量可能的优化;volatitle 这个关键字表述的问题一般都比较底层。

    应用场景:

    1) 并行设备的硬件寄存器(如:状态寄存器)几乎所有的 GPIO 的描述都包含这个关键字;

    #define inp(port) (((volatile byte ) (port)))

    2) 一个中断服务子程序中会访问到的非自动变量

    3) 多线程应用中被几个任务共享的变量

  5. 5

    const_cast类型转换举例:

    #include <iostream>

    using std::cout;

    using std::endl;

    void change(const int * pt, int n )

    {

        int *pc;

        pc = const_cast<int *>(pt);

        *pc += n;

    }

    //

    int main( )

    {

        int  pop1 = 38383;

        const int pop2 = 2000;

        cout << "pop1, pop2: << pop1 << "," << pop2 << endl;

        change(&pop1, -103);

        change(&pop2, -103);

        cout << "pop1, pop2:" << pop1 << "," << pop2 << endl;

    }

    //----------------- Out put: -----------------//

    pop1,pop2:38383,2000

    pop1,pop2:38280,2000

    END

static_cast 类型转换运算符

  1. 1

         static_cast运算符的语法与其他类型转换运算符相同:

               static_cast<type-name> (expression)

          仅当type-name可被隐式转换为expression所属的类型expression可被隐式转换为type-name所属的类型时,上述转换才是合法的,否则将出错。假设High是Low的基类,而Pond是一个无关的类,则从High到Low的转换、从Low到High的转换都是合法的,而从Low到Pond的转换是不允许的:

        High bar;

        Low  blow;

        ...

        High *pb = static_cast<High *> (&blow);   //valid upcast

        Low  *pl = static_cast<Low *> (&bar);        //valid downcast

        Pond *pmer = static_cast<Pond *> (&blow);  //invalid, Pond unrelated

           第一种转换是合法的,因为向上转型可以显示地进行。

           第二种转换是从基类指针到派生类指针,在不进行显示类型转换的情况下,将无法进行。但由于无须进行类型转换,便可以进行另一个方向的类型转换。因此使用static_cast来进行向下转换是合法的。

           第三种转换是无法满足前面加粗文字描述的规则,所以是非法的转换。

  2. 2

          同样,由于无须进行类型转换,枚举值就可以被转换成为整型,所以可以用static_cast将整型转换为枚举值。可以使用static_cast将double转换为int、将float转换为long以及其它各种数值转换。

    END

reinterpret_cast 类型转换运算符

  1. interpret    [美]ɪnˈtɜ:rprɪt        vt.解释;

    reinterpret    [美]ˌri:ɪnˈtɜ:rprɪt         vt.重新解释

           摆上两个单词是想表达reinterpret_cast类型转换运算符的作用,其作用就如其名——将一种类型重新转换为另一种类型(两种类型甚至毫无关系)。

           reinterpret_cast运算符用于天生危险的类型转换。它不允许删除const,但会执行其它令人生厌的操作。有时候程序员必须做一些依赖实现的、令人生厌的操作,使用reinterpret_cast运算符可以简化对这种行为的跟踪工作。该运算符的语法与另外3个相同:

                  reinterpret_cast<type-name> (expression);    

  2. 下面是一个使用示例:

    struct dat

    {

          short a;

          short b;

    };

    long value = 0xA224B118;  // long即long int简写

    dat  *pd = reinterpret_cast<dat *> (&value);

    cout << std::hex << pd->a;   //display first 2 bytes of value

           通常,这样转换适用于依赖实现的底层编程技术,是不可移植的。例如,不同的系统存储多字节整型时,可能以不同的顺序存储其中的字节。

  3. 3

          然而,reinterpret_cast运算符并不支持所有的类型转换。例如,可以将指针类型转换为足以存储指针表示的整型,但不能将指针转换为更小的整型或浮点型。另一个限制是,不能将函数指针转换为数据指针,反之亦然。

    END

小结

  1. 1

           在C++中,普通类型转换也受到限制。基本上,可以执行其它类型转换可以执行的操作,加上一些组合,如static_cast或reinterpret_cast后跟const_cast,但不能执行其它转换。因此,线面的类型转换在C语言中是允许的,但在C++中通常不允许,因为对于大多数C++实现,char类型都太小,不能存储指针:

                char ch = (char) &d;

          这些限制是合理的,如果您觉得这种限制难以忍受,可以使用C语言。


<<<<<<<<<<<<<<C++类型转换小结>>>>>>>>>>>>>>

http://blog.youkuaiyun.com/just_kong/article/details/8044662

1.C和C++类型转换

    C风格的强制转换:

           T(exdivssion)或者(T)exdivssion

     C++的类型转换用:dynamic_cast,static_cast,reinterdivt_cast,const_cast

2.运行时类型识别(RTTI)

  (1)定义:程序能够使用基类指针或引用来检查这些指针或引用所指的对象的实际派生类型。

  (2)RTTI提供了两个有用的操作符:

    typeid:返回指针或引用所指的实际类型

    dynamic_cast:用于将一个指向派生类的基类指针或引用转换为派生类的指针或引用

  (3)typeid 函数

    该函数的主要作用就是让用户知道当前的变量是什么类型的,比如使用typid(a).name()就能知道变量a是什么类型的。因为typid()函数是一个反回类型为const typid_info&类型的函数,所以下面先对type_info类作下介绍

  (4)type_info类

    该类的具体实现方式依编译器而定,但一般都有如下的成员定义

  class type_info

  {private:

  type_info(const type_info &);

  type_info& operator =(const type_info&); //type_info类的复制构造函数赋值运算符是私有的。

  public:

  virtual ~type_info(); //析构函数

  bool operator = =(const type_info&) const; //在type_info类中重载了= =运算符,该运算符可以比较两个对象的类型是否相等。

  bool operator !=(const type_info&)const; //重载的!=运算符,以比较两个对象的类型是否不相等

  const char * name() const; //使用得较多的成员函数name,该函数反回对象的类型的名字。前面使用的typeid(a).name()就调用了该成员函数

  bool before(const type_info&);};

  因为type_info类的复制构造函数和赋值运算符都是私有的,所以不允许用户自已创建type_info的类,比如type_info A;错误,没有默认的构造函数。唯一要使用type_info类的方法就是使用typeid函数。

    (5)typeid的使用方式

  1)使用type_info类中的name()成员函数反回对象的类型的名称。其方法为:typeid(object).name()其中object是要显示的对象的类型名,该函数反回的名字因编译器而定。这里要注意的就是使用方式一中提到的虚函数类型的问题,即如果有类A,且有虚函数,类B,C,D都是从类A派生的,且都重定义了类A中的虚函数,这时有类A的指针p,再把对象类B的对象的地址赋给指针p,则typeid(p).name()将反回的类型将是A*,因为这里的p表示的是一个指针,该指针是类型为A的指针,所以反回A*,而typeid(*p).name()将反回B,因为指针p是指向类B的对象的,而*p就表示的是类B的对象的类型,所以反回B。

  2)使用type_info类中重载的= =与!=比较两个对象的类型是否相等。使用该方法需要调用类type_info中重载的= =和!=操作符,其使用方法为typid(object1)= =typid(object2);如果两个对象的类型相等则反回1,如果不相等则为0。这种使用方法通常用于比较两个带有虚函数的类的对象是否相等,比如有类A,其中定义有虚函数,而类B,类C,类D,都是从类A派生而来的且重定义了该虚函数,这时有个类A的指针p和p1,按照虚函数的原理,基类的指针可以指向任何派生类的对象,在这时就有可能需要比较两个指针是否指向同一个对象,这时就可以这样使用typeid了,typeid(*p)= =typeid(*p1);这里要注意的是typeid(*p)与typeid(p)是指的不同的对象类型,typeid(p)表示的是p的类型,在这里p是一个指针,这个指针指向的是类A的对象,所以p的类型是A*,而typeid(*p)则不一样,*p表示的是指针p实际所指的对象的类型,比如这里的指针p指向派生类B,则typeid(*p)的类型为B。所以在测试两个指针的类型是否是相等时应使用*p,即typeid(*p)= =typeid(*p1)。如果是typeid(p)= =typeid(p1)的话,则无论指针p和p1指向的什么派生类对象,他们都是相等的,因为都是A *的类型。

3.C++的四种类型转换

  (1)dynamic_cast

      1)dynamic_cast只能将指向派生类对象的基类指针或引用转换为派生类的指针或引用,若用于其他转换则指针为空,引用则抛出异常。此为向下类型转换。

      2)dynamic_cast转换符只能用于指针或者引用。dynamic_cast转换符只能用于含有虚函数的类。dynamic_cast转换操作符在执行类型转换时首先将检查能否成功转换,如果能成功转换则转换之,如果转换失败,如果是指针则反回一个0值,如果是转换的是引用,则抛出一个bad_cast异常,所以在使用dynamic_cast转换之间应使用if语句对其转换成功与否进行测试,比如pd=dynamic_cast<D*>(pb); if(pd){…}else{…},或者try{};catch(bad_cast){}

      3)使用 typeid 要注意一个问题,那就是某些编译器(如 Visual C++)默认状态是禁用 RTTI 的,目的是消除性能上的开销。如果你的程序确实使用了 RTTI,一定要记住在编译前启用 RTTI。(vc6.0启用方式:project->setting->c/c++->category->c++ Language 下面第二个复选框选中)。使用 typeid 可能产生一些将来的维护问题。假设你决定扩展上述的类层次,从MediaFile 派生另一个叫 LocalizeMedia 的类,用这个类表示带有不同语言说明文字的媒体文件。但 LocalizeMedia 本质上还是个 MediaFile 类型的文件。因此,当用户在该类文件图标上单击右键时,文件管理器必须提供一个“播放”菜单。可惜 build()成员函数会调用失败,原因是你没有检查这种特定的文件类型。为了解决这个问题,你必须象下面这样对 build() 打补丁

  void menu::build(const File * pfile)

  {

  //......

  else if (typeid(*pfile)==typeid(LocalizedMedia))

  {

  add_option("play");

  }

  }

  唉,这种做法真是显得太业余了,以后每次添加新的类,毫无疑问都必须打类似的补丁。显然,这不是一个理想的解决方案。这个时候我们就要用到 dynamic_cast,这个运算符用于多态编程中保证在运行时发生正确的转换(即编译器无法验证是否发生正确的转换)。用它来确定某个对象是 MediaFile 对象还是它的派生类对象。dynamic_cast 常用于从多态编程基类指针向派生类指针的向下类型转换。它有两个参数:一个是类型名;另一个是多态对象的指针或引用。其功能是在运行时将对象强制转换为目标类型并返回布尔型结果。也就是说,如果该函数成功地并且是动态的将 *pfile 强制转换为 MediaFile,那么 pfile的动态类型是 MediaFile 或者是它的派生类。否则,pfile 则为其它的类型:

  void menu::build(const File * pfile)

  {

  if (dynamic_cast <MediaFile *> (pfile))

  {

  // pfile 是 MediaFile 或者是MediaFile的派生类 LocalizedMedia

  add_option("play");

  }

  else if (dynamic_cast <TextFile*> (pfile))

  {

  // pfile 是 TextFile 是TextFile的派生类

  add_option("edit");

  }

  }

  细细想一下,虽然使用 dynamic_cast 确实很好地解决了我们的问题,但也需要我们付出代价,那就是与 typeid 相比,dynamic_cast 不是一个常量时间的操作。为了确定是否能完成强制类型转换,dynamic_cast`必须在运行时进行一些转换细节操作。因此在使用 dynamic_cast 操作时,应该权衡对性能的影响。

  (2)static_cast

      1)完成向上类型转换,即将指向派生类的指针或引用转换为指向同一层次中的一个基类的指针或引用。

      2) 使用例子

[cpp]  view plain  copy
  1. #include <iostream>    
  2. class B   
  3. {       
  4.     int i;    
  5. public:     // Conversion constructor.       
  6.     B(int a) : i(a)     { }       
  7.     void display()    
  8.     {   
  9.         std::cout << i;  
  10.     }   
  11. };    
  12. int main()   
  13. {           
  14.     B bobj1 = (B)123;     // C-style cast int to B.  
  15.     bobj1.display();  
  16.     std::cout << '/';       
  17.     bobj2 = B(456);       // Constructor notation B  
  18.     bobj2.display();       
  19.     std::cout << '/';           
  20.     B bobj3 = static_cast<B>(789);     // Static_cast.   
  21.     bobj3.display();         
  22.     return 0;   
  23. }   

  (3)reinterpret_cast

     reinterpret_cast操作符代替了大多数其它C风格类型转换的使用。reinterpret_cast将指针转换为其它指针类型、将数字转换为指针或将指针转换为数字。与使用C风格的类型转换一样,当使用reinterpret_cast操作符时,用户应该知道自已要干什么。有时,只有C风格的类型转换才能起作用,但这并不是说从来都不应该使用reinterpret_cast。下例展示了一个用void型指针返回100个字符的缓冲区的简单内存分配程序。Main()函数将返回结果赋值给一个字符型指针。C++的转换规则与C并不相同。在C++的转换规则中,不能隐式地将void*转换为char*,因此,需要进行类型转换。下面使用了reinterpret_cast而不是C语言风格的类型转换。

[cpp]  view plain  copy
  1. #include <iostream>   
  2. #include <cstring>    
  3. //Create a buffer. //   
  4. void* getmem()   
  5. {  
  6.  static char buf[100];       
  7.  return buf;   
  8. }    
  9. int main()   
  10. {       
  11.  char* cp = reinterpret_cast<char*>(getmem());       
  12.  strcpy(cp, "Hello, Woody");       
  13.  std::cout << cp;         
  14.  return 0;   
  15. }  

  (4)const_cast

      刚才所讨论的3种类型转换操作都没有涉及“常量属性”,即不能使用它们移去对象的常量属性。为此,我们要使用const_cast操作符。除了const和volatile关键字之外,它的类型参数必须和对象参数的类型相匹配。
      例如,考虑一个全局的计数器,它描述了对常类对象或其它类型的类对象的操作次数。下面给出了这样的一个例子

[cpp]  view plain  copy
  1. #include <iostream>    
  2. class A   
  3. {       
  4.     int val;       
  5.     int rptct;  // Number of times the object is reported.  
  6. public:       
  7.     A(int v) : val(v), rptct(0) { }       
  8.     ~A()   
  9.     {   
  10.         cout << val << " was reported " << rptct << " times.";   
  11.     }       
  12.     void report() const;   
  13. };    
  14. void A::report() const   
  15. {       
  16.     const_cast<A*>(this)->rptct++;       
  17.     cout << val << endl;   
  18. }    
  19. int main()   
  20. {       
  21.     const A a(123);        
  22.     a.report();       
  23.     a.report();       
  24.     a.report();         
  25.     return 0;   
  26. }  

<<<<<<<<<<<<<<C++类型转换注意事项>>>>>>>>>>>>>>

http://blog.youkuaiyun.com/yhhwatl/article/details/24298415

C++ 在设计中一直强调类型安全,而且也采取了一定的措施来保障这条准则的执行。但是,从C继承而来的强制转型却破坏了C++类型系统,C中的强制转型可谓是“无所不能”,其超强的能力给C++带来了很大的安全隐患。强制转型会引起各种各样的麻烦,有时这些麻烦很容易被察觉,有时它们却又隐藏极深,难以察觉。

在C/C++语言中,强制转型是“一个你必须全神贯注才能正确使用”的特性。所以一定要慎用强制转型。

首先来回顾一下C 风格(C-style)的强制转型语法,如下所示:

 
  1. // 将表达式的类型转换为 T  
  2. (T) expression  
  3. T(expression) 

这两种形式之间没有本质上的区别。在C++中一般称为旧风格的强制转型。

在赋值时,强制类型的转换形式会让人觉得不精密、不严格,缺乏安全感,主要是因为不管表达式的值是什么类型,系统都自动将其转为赋值运算符左侧变量的类型。而转变后数据可能会有所不同,若不加注意,就可能产生错误。

将较大的整数转换为较短的数据类型时,会产生无意义的结果,而程序员可能被蒙在鼓里。正如下面的代码片段所示:

 
  1. unsigned i = 65535;  
  2. int j = (int) i; 

输出结果竟然成了-1。较长的无符号类型在转换为较短的有符号类型时,其数值很可能会超出较短类型的数值表示范围。编译器不会监测这样的错误,它所能做的仅仅是抛出一条非安全类型转换的警告信息。如果这样的问题发生在运行时,那么一切会悄无声息,系统既不会中断,也不会出现任何的出错信息。

类似的问题还会发生在有符号负数转化为无符号数、双精度类型转化为单精度类型、浮点数转化为整型等时候。以上这些情况都属于数值的强制转型,在转换过程中,首先生成临时变量,然后会进行数值截断。

在标准C中,强制转型还有可能导致内存扩张与截断。这是因为在标准C中,任何非void类型的指针都可以和void类型的指针相互指派,也就可以通过void类型指针这个中介,实现不同类型的指针间接相互转换了。代码如下所示:

 
  1. double PI = 3.1415926;  
  2. double *pd = &PI;  
  3. void *temp = pd;  
  4. int *pi = temp; //转换成功 

指针pd指向的空间本是一个双精度数据,8字节。但是经过转换后,pi却指向了一个4字节的int类型。这种发生内存截断的设计缺陷会在转换后进行内存访问时存在安全隐患。不过,这种情况只会发生在标准C中。在C++中,设计者为了杜绝这种错误的出现,规定了不同类型的指针之间不能相互转换,所以在使用纯C++编程时大可放心。而如果C++中嵌入了部分C代码,就要注意因强制转型而带来的内存扩张或截断了。

与旧风格的强制转型相对应的就是新风格的强制转型了,在C++提供了如下四种形式:

 
  1. const_cast(expression)  
  2. dynamic_cast(expression)  
  3. reinterpret_cast(expression)  
  4. static_cast(expression) 

新风格的强制转型针对特定的目的进行了特别的设计,如下所示。

const_cast<T*> (a)

它用于从一个类中去除以下这些属性:const、volatile和 __unaligned。

 
  1. class A  {  // …  };  
  2. void Function()  
  3. {  
  4.     const A *pConstObj = new A;  
  5.     A *pObj = pConstObj; //ERROR: 不能将const对象指针赋值给非const对象  
  6.   pObj = const_cast<A*>( pConstObj); // OK  
  7.     //...  

这种强制转型的目的简单明确,使用情形比较单一,易于掌握。

dynamic_cast<T*>(a)

它将a值转换成类型为T的对象指针,主要用来实现类层次结构的提升,在很多书中它被称做“安全的向下转型(Safe Downcasting)”,用于继承体系中的向下转型,将基类指针转换为派生类指针,这种转换较为严格和安全。如下面的代码片段所示:

 
  1. class B {  //...  };  
  2. class D : public B {  //...  };  
  3. void Function(D *pObjD)  
  4. {  
  5.      D *pObj = dynamic_cast<D*>( pObjD);  
  6.      //...  

如果pObjD指向一个D类型的对象,pObj则指向该对象,所以对该指针执行D类型的任何操作都是安全的。但是,如果pObjD指向的是一个B类型的对象,pObj将是一个空指针,这在一定程度上保证了程序员所需要的“安全”,只是,它也付出了一定的运行时代价,而且代价非常大,实现相当慢。有一种通用实现是通过对类名称进行字符串比较来实现的,只是其在继承体系中所处的位置越深,对strcmp的调用就越多,代价也就越大。如果应用对性能要求较高,那么请放弃dynamic_cast。

reinterpret_cast<T*>(a)

它能够用于诸如One_class* 到 Unrelated_class*这样的不相关类型之间的转换,因此它是不安全的。其与C风格的强制转型很是相似。

 
  1. class A { // ... };  
  2. class B { //... };  
  3. void f()  
  4. {  
  5.   A* pa = new A;  
  6.   B* pb = reinterpret_cast<B*>(pa);  
  7.   // ...  

在不了解A、B内存布局的情况下,强行将其进行转换,很有可能出现内存膨胀或截断。

static_cast<T*>(a)

它将a的值转换为模板中指定的类型T。但是,在运行时转换过程中,它不会进行类型检查,不能确保转换的安全性。如下面的代码片段所示:

 
  1. class B { ... };  
  2. class D : public B { ... };  
  3. void Function(B* pb, D* pd)  
  4. {  
  5.     D* pd2 = static_cast<D*>(pb);   // 不安全  
  6.     B* pb2 = static_cast<B*>(pd);   // 安全的  

之所以说第一种是不安全的,是因为如果pb指向的仅仅是一个基类B的对象,那么就会凭空生成继承信息。至于这些信息是什么、正确与否,无从得知。所以对它进行D类型的操作将是不安全的。

C++是一种强类型的编程语言,其规则设计为“保证不会发生类型错误”。在理论层面上,如果希望程序顺利地通过编译,就不应该试图对任何对象做任何不安全的操作。不幸的是,继承自C语言的强制转型破坏了类型系统,所以建议尽量少地使用强制转型,无论是旧的C风格的还是新的C++风格的。如果发现自己使用了强制转型,那么一定要小心,这可能就是程序出现问题的一个信号。

请记住:

由于强制转型无所不能,会给C++程序带来很大的安全隐患,因此建议在C++代码中,努力将强制转型减到最少。


<<<<<<<<<<<<<<C++中类型转换规则>>>>>>>>>>>>>>

<分析>:任何一个操作符在运算之前其操作数的类型必须一致,如果不一致必须进行类型转换,对不同类型之间的转换规则总结如下:

(1)整型提升:

  在算术运算中,对于所有比int型小的整型,包括char,signed char,unsigned char,short,unsigned short,如果该类型所有可能值都包含在int内,它们就被提升为int,否则将被提升为unsigned int。也就是在计算前不管算式中有无int,都会被提升为int。

(2)如果算式中存在同一类型的有符号和无符号的表达式,signed的类型被转换为unsigned。

(3)类型转换是确保精度优先的条件下进行的(long double>double>float>unsigned long>long>unsigned int>int)。

(4)任何数据类型的指针都可以转换为void*类型,整数0可以转换为任何指针类型。

(5)枚举对象或者枚举成员转换为整型时至少提升为int型。

(6)非const对象可以初始化const对象,同时可以将非const类型对象的地址转化为执行const类型的指针。

(7)强制类型转换:

   ①daynamic_cast:运行时识别指针或者引用所指向的对象。实现类继承对象之间的转换,可以将基类类型引用或者指针安全的转换为派生类引用或者指针;

   ②const_cast:转换掉表达式中的const性质;

   ③static_cast:隐式执行的任何类型转换都可以由static_cast显示完成,可以用这个转换找回存放在void指针中的值;

   ④reinterpret_cast,实现本来不能转换的类进行转换,是不安全的数据类型转换;

(8)类类型转换:

   ①类类型转换前后都可以有标准类型转换;

   ②一个表达式中只能有一个类类型转换;

(9)新式强制类型转换的优点:

   ①很容易在代码中被辨认出来,因而得以简化“找出类型系统的哪个地点被破坏”的过程。

   ②各转型动作目标越狭窄,编译器越能诊断出错误的运用。

   ③旧的类型转换任何类型都可以转换,但是不安全。

(10)如果可以,尽量避免转型,因为像dynamic_cast这样的转型很浪费时间,如果必须转换,尽量把转型放在函数里,客户可以调用函数,而不是将转型放在自己的代码里。

<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值