🥝类型转换
2022-5-1
| 2023-8-2
0  |  阅读时长 0 分钟
type
status
date
slug
summary
tags
category
icon
password
Property

C++ 中的类型之间存在转换时被认为是类型相关,当需要其中一种类型值时,可以用另一种类型值替换。
  1. 隐式类型转化(截断或提升):编译器在编译阶段自动进行,能转就转,不能转就编译失败
  1. 显式类型转化(强转):需要用户自己处理
 

隐式转换

以下情况将发生隐式转换:
  • 在大多数表达式中,比int类型小的整型值首先提升为较大的整数类型
  • 在条件部分,非bool值将首先转为 bool
  • 在初始化过程中,初始值转换成变量的类型;在赋值语句中,右侧运算对象转换成左侧运算对象的类型
  • 在算数和条件表达式中,操作数的类型转为一个共同的不丢失精度的类型
  • 函数调用时实参类型转为形参类型
 
算术转换
把一种算术类型转换成另一种算术类型叫做算术转换,其中运算符的运算对象将被转换成最宽的类型。
整型提升负责把小整数类型转换成较大的整数类型。
如果某个运算符的运算对象类型不一致, 这些运算对象将转换成同一种类型。但是如果某个运算对象的类型是无符号类型,那么转换的结果就要依赖于机器中各个整数类型的相对大小了。
 
 
其它隐式转换
  • 数组转换成指针:在大多数表达式中,数组名字自动转换成指向数组首元素的指针
    • 指针转化:常量整数值0或字面值 nullptr能转换成任意指针类型;指向任意非常量的指针能转换成 void*;指向任意对象的指针能转换成 const void*
    • 转换成布尔类型:任意一种算术类型或指针类型都能转换成布尔类型。如果指针或算术类型的值为0,转换结果是 false,否则是 true
      • 转换成常量:允许将指向非常量类型的指针转换成指向相应的常量类型的指针,对于引用也是这样
        • 由类定义的转换:类可以通过定义单参数的构造函数来实现由其参数类型到类类型的转换,除非显式禁止,编译器会自动进行如此转换。编译器类的直接初始化转换,如果需要通过两步以上的转换才能转为需要的类型,编译器会拒绝;
           
           
           

          C 风格强转

          C风格强转形如:
          C风格的转换格式很简单,但是有不少缺点的:
          1. 隐式类型转化有些情况下可能会出问题:比如数据精度丢失
          1. 显式类型转换将所有情况混合在一起,代码不够清晰
          因此C++提出了自己的类型转化风格,因为C++要兼容C语言,所以C++中还可以使用C语言的转化风格。
           
           

          C++显式转换

          显式转换被C++重新定义为更加精细的多种不同强转,当然C风格的强转也可以使用。C++中定义了多种具名强转,形如: 

          static_cast

          这种强转主要用于定义良好的转换,如:算数类型之间的转换,将 void* 指针转为实际的类型指针,以及指向基类的指针转为指向子类的指针,或者可以用 expression 直接初始化 type ,将任何类型转为 void 类型,将枚举转为整数或者相反。如果本身不能这样转换,但是使用了的话则行为是未定义的。对于这种转换,C++ 语言本身不做任何运行时安全检查。相对的,dynamic_cast 将会对转换类型进行运行时检查,确保确实可以从基类指针转为子类指针。
          static_cast用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用,不能用于两个不相关的类型进行转换
           

          const_cast

          用于改变操作数的底层const,最常用的是去掉const修饰。一旦去掉了const,编译器将不做任何检查,如果对象本身就是一个const的,试图改写这个对象将是未定义的。只能通过const_cast来改变对象或表达式的const属性,如果用别的具名强转将产生编译时错误,同样不能用const_cast改变表达式类型。
           
          为什么取消了a的const属性,并后续通过修改*p的方式,为何a还是2呢?
          这里设计到了编译器的优化,编译器默认cosnt修饰的变量是不会被修改的,因此会将cosnt修饰的变量a放到寄存器中,当需要读取const变量时就会直接从寄存器中读取,但是实际修改的是内存中a的值,所以最终导致输出的a是未修改前的值2。
          如果非要修改a呢?
          如果不想让编译器将const变量优化到寄存器当中,只需要加一个关键字volatileconst变量进行修饰即可,这个关键字的作用是让编译器强制去内存中读取,这样就能看到修改后的结果了。
           
          在C语言中,没有const_cast,但是C语言是通过强制类型转换的方式完成上述目的的:
           

          reinterpret_cast

          reinterpret_cast将执行位模式的重新解释,这样可以将一个类型转为任何别的类型。
          主要用于内置类型的转换,运用于非内置类型可以肯定是错误的:
          接下来的操作将会假设 pc 指向一个 char 类型值,编译器将无法对真实类型做检查。好处在于灵活性,特别是处理网络层读写时经常用到。坏处则是这种操作非常之危险,一朝不慎,整个程序崩溃。
           
           
           

          dynamic_cast

          dynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换),即向下转换,当然也有向上转换:
          • 向上转型:子类对象指针/引用->父类指针/引用(不需要转换,赋值兼容规则,天然支持)
          • 向下转型:父类对象指针/引用->子类指针/引用(用dynamic_cast转型是安全的)
          注意:
          1. dynamic_cast只能用于父类含有虚函数的类
          1. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0
           
          向上转换就是切割、切片,是语法天然支持的,不需要进行转换,而向下转换是语法不支持的,需要进行强制类型转换,并且只有指针和引用才支持向下转换,对象不支持。为什么要支持向下转换呢?
          fun函数中的父类指针pa到底是指向父类的对象,还是指向子类的对象呢?针对这两种情况我做出下面的讨论:
          • 如果父类的指针(或引用)指向的是一个父类对象,那么将其转换为子类的指针(或引用)是不安全的,因为转换后可能会访问到子类的资源,而这个资源是父类对象所没有的。
          • 如果父类的指针(或引用)指向的是一个子类对象,那么将其转换为子类的指针(或引用)则是安全的。
          使用C语言的强制类型转换进行向下转型是不安全的,因为此时无论父类的指针(或引用)指向的是父类对象还是子类对象都会进行转换。而使用dynamic_cast进行向下转型则是安全的,如果父类的指针(或引用)指向的是子类对象那么dynamic_cast会转换成功,但如果父类的指针(或引用)指向的是父类对象那么dynamic_cast会转换失败并返回一个空指针:
           
           
           
           
        • C++
        • 运算符简单语句和作用域
          目录