在C++中,类型转换(Type Conversion)是将一种数据类型转换为另一种数据类型的过程。类型转换可以分为两大类:隐式转换(implicit conversion)和显式转换(explicit conversion)。文章源自灵鲨社区-https://www.0s52.com/bcjc/cyyjc/16538.html
1. 隐式转换
隐式转换是由编译器自动完成的,不需要显式的类型转换操作符。这种比较简单。常见的隐式转换有以下几种:文章源自灵鲨社区-https://www.0s52.com/bcjc/cyyjc/16538.html
1.1 整形提升
char
、short
等较小的整数类型可以自动提升为 int
。文章源自灵鲨社区-https://www.0s52.com/bcjc/cyyjc/16538.html
cpp
char c = 'A';
int i = c; // char 自动提升为 int
1.2 算术转换
不同类型的数据在混合运算时会进行转换,以确保运算的正确性。例如,int
和 double
进行运算时,int
会被转换为 double
。文章源自灵鲨社区-https://www.0s52.com/bcjc/cyyjc/16538.html
cpp
int i = 10;
double d = 3.14;
double result = i + d; // int 被转换为 double
2. 显示转换
以上都属于C风格的转换格式,简单但是有一些问题,文章源自灵鲨社区-https://www.0s52.com/bcjc/cyyjc/16538.html
- 隐式类型转化有些情况下可能会出问题:比如数据精度丢失。
- 显式类型转换将所有情况混合在一起,代码不够清晰。
因此C++提出了自己的类型转化风格,注意因为C++要兼容C语言,所以C++中还可以使用C语言的转化风格。文章源自灵鲨社区-https://www.0s52.com/bcjc/cyyjc/16538.html
标准C++为了加强类型转换的可视性,引入了四种命名的强制类型转换操作符:文章源自灵鲨社区-https://www.0s52.com/bcjc/cyyjc/16538.html
static_cast
reinterpret_cast
const_cast
dynamic_cast
2.1 static_cast
static_cast
用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用 static_cast
,但它不能用于两个不相关的类型进行转换。文章源自灵鲨社区-https://www.0s52.com/bcjc/cyyjc/16538.html
cpp
int main()
{
double d = 12.34;
int a = static_cast<int>(d);//将d转换为ind
std::cout << a << std::endl;//输出12
return 0;
}
两个不相关的类进行转换,会出错。如下:文章源自灵鲨社区-https://www.0s52.com/bcjc/cyyjc/16538.html
cpp
int main()
{
double d = 12.34;
int a = static_cast<int>(d);//将d转化为int
std::cout << a << std::endl;//输出12
int* p = static_cast<int*>(a);//将a转化为int*类型 error
return 0;
}
2.2 reinterpret_cast
reinterpret_cast
操作符通常为操作数的位模式提供较低层次的重新解释,用于将一种类型转换为另一种不同的类型。文章源自灵鲨社区-https://www.0s52.com/bcjc/cyyjc/16538.html
cpp
int main()
{
double d = 12.34;
int a = static_cast<int>(d);//将d转化为int
std::cout << a << std::endl;//输出12
//int* p = static_cast<int*>(a);//将a转化为int*类型
int* p = reinterpret_cast<int*>(a);//将a转化为int*类型
return 0;
}
reinterpret_cast不进行任何类型安全检查,因此可能会导致未定义行为。它只改变对象的解释方式而不改变对象的存储内容。应尽量减少使用。
2.3 const_cast
const_cast
最常用的用途就是删除变量的const
属性,方便赋值。
cpp
int main()
{
const int a = 2;
int* p = const_cast<int*>(&a);
*p = 3;
std::cout << a << std::endl;
std::cout << *p << std::endl;
return 0;
}
注意:在C++标准中,试图修改一个
const
对象的值会导致未定义行为。这意味着编译器对这个操作不会进行任何保障,程序的结果可能是不确定的,可能导致崩溃、不可预期的输出,甚至可能在不同的编译器或平台上表现出不同的行为。
上面的代码运行结果:
内存中存放的是3,但是打印显示的是2。
这种属于未定义行为,未定义行为(Undefined Behavior, UB)是指程序的行为没有明确定义的情况,这意味着编译器对代码的行为没有任何规定,程序可以表现出任何结果。会导致程序崩溃红藕这生成错误的结果,看似正常但潜在有安全隐患。应该理解并避免未定义行为。
2.4 dynamic_cast
dynamic_cast
用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)。
子类转父类 直接转就行了~ 不需要转换,赋值兼容规则。
dynamic_cast
只能用于父类含有虚函数的类。dynamic_cast
会先检查是否能转换成功,能成功则转换,不能则返回0。
cpp
//父类Base
class Base
{
public:
Base() = default;
Base(const Base& b) = default;
virtual ~Base() = default;
void show()
{
std::cout << "Hello Base" << std::endl;
}
};
//子类Drivaer
class Drivaer : public Base
{
public:
Drivaer()
:Base()
{
}
void show()
{
std::cout << "Hello Drivaer" << std::endl;
}
};
int main()
{
Base* b = new Base;
Drivaer* d = new Drivaer;
//将父类指针b转换为子类指针类型
if (Drivaer* bb1 = dynamic_cast<Drivaer*>(b))
{
bb1->show();
}
else
{
std::cout << "dynamic_cast fail" << std::endl;
}
if (Drivaer* bb2 = static_cast<Drivaer*>(b))
{
bb2->show();
}
else
{
std::cout << "static_cast fail" << std::endl;
}
}
上面程序运行结果:
- 使用
dynamic_cast
转换失败
因为 b 指向的是 Base 类对象,
dynamic_cast
返回nullptr
。在使用dynamic_cast
进行类型转换时,如果转换不安全(即基类指针指向的对象不是目标派生类的对象),dynamic_cast
将返回nullptr
。
必须让父类指针指向子类的对象才可以转换成功。
static_cast
转换成功
static_cast
允许将指针或引用从一个类型转换为另一个相关类型,但不会进行运行时类型检查。因此,如果static_cast
用于将父类指针或引用转换为子类指针或引用,可能会导致未定义行为(如访问不存在的派生类成员或对象切片问题)。
3. 总结
在 C++ 中,显示转换(Explicit Type Conversion)是通过使用不同的类型转换操作符来实现的,每种操作符都有其特定的用途和适用条件。
- static_cast
- 用于编译时类型转换。
- 支持较宽泛的转换,如整数之间的类型转换、相关类型的指针或引用转换(但不涉及底层 const 属性的转换)、向上和向下的类层次转换(但不进行运行时类型检查)。可能引发未定义行为,慎用。
- 不能用于在不相关的类之间进行转换。
- reinterpret_cast
- 用于将一个指针转换为另一个类型的指针,或者将一个指针转换为一个整数类型,反之亦然。
- 非常危险,潜在地引入未定义行为。
- 主要用于低级编程和与硬件相关的编程场景。
- const_cast
- 用于移除对象的
const
或volatile
修饰 - 主要用于在函数中修改传入参数的
const
属性。 - 如果使用
const_cast
来修改一个原本声明为const
的对象,而该对象实际上是const
的(例如通过字面常量或者通过const
对象的指针进行转换),则会导致未定义行为。慎用。
- 用于移除对象的
- dynamic_cast
- 用于运行时类型检查,主要用于类层次间的安全向下转型(Downcasting)
- 只能用于含有虚函数的类(多态类型)。
- 如果转换不安全,返回
nullptr
(对于指针)或抛出bad_cast
异常(对于引用)。
总的来说,要避免未定义行为,应当遵循以下原则:
- 尽量避免使用
reinterpret_cast
,除非绝对必要且非常清楚其风险。 - 在使用
const_cast
时,确保转换是安全的,并且不会尝试修改本来应该是const
的对象。 - 使用
static_cast
和dynamic_cast
时,确保类型之间有合理的转换关系,并避免不相关类型之间的转换。父类转子类安安全全使用dynamic_cast
。
评论