类型转换这个概念在许多编程语言中都有涉及,比如最经典的编程语言:C
语言,它对类型转换的处理可视性比较差,所有的转换形式都是以同一种相同形式书写:在变量前声明转换类型,这样的方式难以跟踪错误的转换。
举例:
int main(){
int i = 1;
double d = i; // 隐式类型转换
printf("%d, %.2f\n", i, d);
int* p = &i;
int address = (int)p; // 显示的强制类型转换
printf("%x, %d\n", p, address);
}
输出结果:
1, 1.00
28fb78, 268516
代码虽然会有警告格式字符串“%x”需要类型“unsigned int”的参数,但可变参数 1 拥有了类型“int *”
,但是编译没有问题,还是可以正常运行。
下面引入C++
中的强制类型转换方式:
标准C++
为了加强类型转换的可视性,引入了四种命名的强制类型转换操作符:
static_cast
reinterpret_cast
const_cast
dynamic_cast
static_cast
用于非多态类型的转换(静态转换)
编译器隐式执行的任何类型转换都可static_cast
,但它只能用于两个相关类型的转换,不相关类型不能转换。
(何为不相关?例如int
与int*
,编译器对于二者的理解没有强相关性,一个为整型数据,一个为地址,所以不可以使用static_cast
进行强制转换)。
用法:
int main(){
double d = 88.48;
int a = static_cast<int>(d);
cout << a << endl;
return 0;
}
输出结果:
88
很明显static_cast
将同为实数类型的整型与浮点数进行了强制转换。
reinterpret_cast
操作符通常为操作数的位模式提供较低层次的重新解释,用于将一种类型转换为另一种不同的类型。
reinterpret_cast
将数据的二进制形式重新解释,但是不改变其值:
int main() {
int i;
const char* ptr = "1";
i = reinterpret_cast<char>(ptr);
std::cout << i << std::endl;
system("pause");
return 0;
}
输出结果:
64
警告:“reinterpret_cast”: 从“const char *”到“char”截断
。
const_cast
的用途就是删除变量的const
属性,方便赋值,例如把const
类型的指针变为非const
类型的指针。
int main() {
const int a = 21;
int* modifier = const_cast<int*>(&a);
*modifier = 7;
cout << *modifier << endl;
return 0;
}
输出结果:
7
这里要提一个没有使用过的关键字:volatile
:保持内存可见性,防止编译器过度优化。
volatile const int a = 21;
volatile
关键字之后发生了什么?const
关键字使得编译器默认a
变量是const
类型,所以编译读取一次a
的值之后就默认这个值不会改变了,对a
的读取操作也会直接从寄存器上取出使用这个值,等效替代读取a
。如果是修改就没有影响。a
中的7
,进行输出。dynamic_cast
用于将一个父类对象的指针转换为子类对象的指针或引用(也称动态转换)
dynamic_cast
转型是安全的。注意:
dynamic_cast
只能用于含有虚函数的类。dynamic_cast
会先检查是否能转换成功,能成功则转换,不能则返回0
。class A{
public:
virtual void f() {}
};
class B : public A
{};
void fun(A* pa){
// dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回
B* pb1 = static_cast<B*>(pa);
B* pb2 = dynamic_cast<B*>(pa);
cout << "pb1:" << pb1 << endl;
cout << "pb2:" << pb2 << endl;
}
int main(){
A a;
B b;
fun(&a);
fun(&b);
return 0;
}
输出结果:
pb1:0019F8F0
pb2:00000000 //未转换成功
pb1:0019F8E4
pb2:0019F8E4
其实 dynamic_cast
是一个泛型
template<class PDirve, class Base>
PDirve my_dynamic_cast(Base* p){
if (p->is_base()){
return nullptr;
}
else{
return (PDirve)p;
}
}
既然容易出问题,那么还是建议防患于未然:避免使用强制类型转换。
explicit
关键字阻止经过转换构造函数进行的隐式转换的发生。
class A{
public:
explicit A(int a){
cout << "A(int a)" << endl;
}
A(const A& a){
cout << "A(const A& a)" << endl;
}
private:
int _a;
};
int main(){
A a1(1); // 隐式转换-> A tmp(1); A a2(tmp);
A a2 = 1;
}
程序会报错:没有int
到A
的适当的构造函数,因为我们通过explicit
禁止了它的单参数构造。
虽然C
风格的转换格式很简单,但是有不少缺点的:
所以C++
的强制类型转换还是有用武之地的。
RTTI
:(Run-time Type identification
)即:运行时类型识别。
C++
通过以下方式来支持RTTI
:
typeid
运算符。dynamic_cast
运算符。