C++为什么要引入四种类型转换? #
众所周知,C++ 关于类型转换引入了四种方式:static_cast
、const_cast
、dynamic_cast
、reinterpret_cast
。
为什么要引入这几种类型转换? #
C++ 完全兼容 C 语言,而 C 语言的类型转换非常灵活,可以在任意类型之间转换。然而,这种灵活性也带来了安全隐患。例如,可能将指向常量对象的指针转换为非常量对象的指针,或者将基类对象指针错误地转换为派生类对象指针。这些转换容易引入难以排查的bug,且需要仔细的代码审查才能发现。
C++ 引入这几种类型转换,可以在不同场景下使用不同的转换方式,从而提高代码的安全性和可维护性,同时也有利于代码审查。
四种类型转换的使用场景 #
static_cast
#
使用方式:
#include <iostream>
using namespace std;
struct Base {
virtual void Func() { cout << "Base Func \n"; }
};
struct Derive : public Base {
void Func() override { cout << "Derive Func \n"; }
};
int main() {
float f = 1.23;
cout << "f " << f << endl;
int i = static_cast<int>(f);
cout << "i " << i << endl;
Derive d;
d.Func();
Base *b = static_cast<Base*>(&d);
b->Func();
return 0;
}
使用场景:
- 基本数据类型之间的转换,例如
float
转int
,int
转char
等。 - 在有类型指针和
void*
之间转换。 - 子类对象指针转换成父类对象指针。
static_cast
适用于非多态类型的转换,建议将所有的隐式类型转换用 static_cast
显示替换。不能用于不兼容的指针类型转换,例如将 float*
转换为 int*
。
dynamic_cast
#
使用方式:
#include <iostream>
using namespace std;
struct Base {
virtual void Func() { cout << "Base Func \n"; }
};
struct Derive : public Base {
void Func() override { cout << "Derive Func \n"; }
};
int main() {
Derive d;
d.Func();
Base *b = dynamic_cast<Base*>(&d);
b->Func();
Derive *dd = dynamic_cast<Derive*>(b);
dd->Func();
return 0;
}
使用场景:
- 将父类的指针或引用转换为子类的指针或引用。
- 父类必须要有虚函数,因为
dynamic_cast
在运行时检查,需要运行时类型信息(RTTI),而 RTTI 存储在虚函数表中。
const_cast
#
使用方式:
int main() {
int data = 10;
const int *cpi = &data;
int *pi = const_cast<int*>(cpi);
const int *cpii = const_cast<const int*>(pi);
return 0;
}
使用场景:
- 常量指针或引用与非常量指针或引用之间的转换。
const_cast
是唯一可以对常量进行操作的类型转换,通常用于去除常量性。去除常量性是危险操作,需谨慎使用。
reinterpret_cast
#
使用方式:
int main() {
int data = 10;
int *pi = &data;
float *fpi = reinterpret_cast<float*>(pi);
return 0;
}
使用场景:
- 类似 C 语言中的强制类型转换,可以转换任意类型。
- 万不得已时才使用,因为这种转换方式不安全,容易引入 bug。
总结 #
C++ 引入 static_cast
、const_cast
、dynamic_cast
和 reinterpret_cast
,是为了在不同场景下提供更安全、更明确的类型转换方式,同时便于代码审查和维护。虽然 reinterpret_cast
类似于 C 语言的强制类型转换,但由于其不安全性,应尽量避免使用。