前言
显式类型转换(强制类型转换)并不能解决隐式转换中可能出现的精度损失或符号丢失等问题,而是用来让程序员可以清晰地表达他们的转换意图,同时在某些情况下允许进行那些隐式转换所不允许的操作
隐式类型转换
隐式类型转换往往是编译器实现的,这种转换会导致预期之外的行为,比如精度丢失、溢出、意外的类型提升等问题。这些问题有时可能难以在代码审查或测试中发现,因为它们不会产生编译错误,例如:
- 精度损失:当把一个浮点数赋值给一个整数类型时,小数部分会被丢弃,导致精度损失。
double d = 9.99;
int i = d; // i 将成为 9,小数部分丢失。
- 符号丢失:将一个有符号类型赋值给无符号类型时,如果原数值为负,则会导致不预期的大正数。
int i = -1;
unsigned int ui = i; // ui 会变成一个很大的正数,而不是-1。
隐式类型转换之所以不安全,主要是因为它发生时不需要程序员的明确指示,而导致一些意外的后果被忽视。
为什么需要强制类型转换
首先明确一点,强制类型转换并不能规避隐式类型转换中的问题,它的目的在于提升程序的可读性以及可维护性,通过显式类型转换,程序员能够清楚地知道数据类型的转换发生了,从而有意识地处理可能出现的问题,比如通过添加额外的检查或使用其他机制来避免精度损失和溢出等问题。
总结一下就是三点:
- 明确指出转换的发生,提高代码可读性和可维护性
- 某些情况下允许隐式转换所不允许的操作发生
- 如果检测到当前转换是不安全的,它会报错或提供警告
static_cast
static_cast用于执行静态类型转换。它比C语言风格的类型转换更加安全,因为它提供了编译时类型检查,帮助避免不安全的转换,但是注意它不提供运行时类型转换的检查
1.使用时机
- 当需要在基本数据类型之间转换时,推荐使用static_cast,以确保类型安全。
- 当需要在类层次结构中进行上行或确保安全的下行转换时。
- 当需要将void*指针转换为具体类型的指针时。
- 当需要显式调用类的类型转换运算符时。
2.使用实例
- 基本数据类型之间的转换
double d = 9.5;
int i = static_cast<int>(d);
- 类的上行转换(从派生类到基类)
static_cast 可以安全地将派生类的指针或引用转换为基类的指针或引用。这种转换是安全的,因为每个派生类对象也是一个基类对象
class Base {};
class Derived : public Base {};
Derived d;
Base* b = static_cast<Base*>(&d); // 将派生类指针转换为基类指针
- void指针转换
可以将void*指针转换为另一种类型的指针,反之亦然,这在与C语言接口交互时特别有用。
int* pi = new int(10);
void* pv = static_cast<void*>(&pi);
int* pi2 = static_cast<int*>(&pv);
dynamic_cast
主要用于多态类型的转换,即在基类指针向派生类指针的转换(或引用),动态转换在运行时进行类型检查,如果转换不合法,会返回空指针或者抛出异常。
在上面的static_cast中介绍了向上转换(从派生类到基类),但是如果想向下转换(从基类到派生类),static_cast执行的操作就是不安全的,这个时候就只能用dynamic_cast
1.使用场景
多态类型的向下转换: 当基类指针或引用指向派生类对象时,如果想要安全地将其转换为派生类指针或引用,可以使用 dynamic_cast。
2.使用方法
指针或引用的转换
dynamic_cast<DerivedType*>(BaseTypePtr);
dynamic_cast<DerivedType&>(BaseTypeRef);
const_cast(谨慎使用)
主要用于添加或去除常量性,例如将 const 类型转换为非 const 类型,或者将非 const 类型转换为 const 类型。常量转换通常用于一些特定的情况下,如在函数中去除参数的 const 修饰符,但是要小心使用,因为其可能会导致未定义行为
使用方法:
指针或引用的转换
const_cast<Type*>(pointer);
const_cast<Type&>(reference);
reinterpret_cast(谨慎使用)
主要用于不同类型之间的位级别转换,例如将一个指针类型转换为另一个不相关的指针类型,或者将指针转换为整数类型。重新解释转换是 C++ 中最不安全的转换,因为它不进行任何类型检查,而是简单地将内存中的位重新解释为其他类型。因此,应该谨慎使用重新解释转换,并确保转换是安全的。
这里解释一下什么叫不同类型的转换,比如说你把一个指针类型转换成整数类型,或者把一个长整型转换成指针类型,这种样的转换就是用reinterpret_cast
示例:
指针转整数,这样做的结果就是把指针里的数据拿出来变成longlong类型,存储在ptrValue中
int num = 10;
int* ptr = #
long long ptrValue = reinterpret_cast<long long>(ptr);
总结
一般情况下对于初级学者来说,使用static_cast和dynamic_cast就够了,常量转换和重新解释转换就尽量避免使用
- static_cast:提供编译时类型转换,常规的数据类型转换用它
- dynamic_cast:提供运行时类型转换,主要用于多态下,基类指针转为派生类指针
- const_cast:用于添加或去除常量属性
- reinterpret_cast:用于指针和普通数据类型间的转换