c语言支持相近类型的隐式类型转换和不相近类型的强制类型转换。
int main()
{
int i = 1;
double d = 8.88;
i = d;//相近类型的隐式类型转化
cout << i << endl;
int* p = nullptr;
p = (int*)i;//不相近类型的强制类型转换
cout << p << endl;
return 0;
}
c++兼容c语言的隐式转换和强制转换,同时也做了一些规范。c++为了加强类型转换的可视性,引入了四种命名的强制类型转换操作符:static_cast、reinterpret_cast、const_cast、dynamic_cast。
static_cast和reinterpret_cast的用法较为简单,分别对应C语言的隐式类型转换和大部分的强制类型转换(用于不相近类型之间,如int和指针),
int main()
{
int i = 1;
double d = 8.88;
d = static_cast<double>(i);//相近类型
int* p = nullptr;
p = reinterpret_cast<int*>(i);//不相近类型
return 0;
}
const_cast可以用于去除变量的const属性,用法如下。通过类型转换可以解引用pi对所指向空间做修改。代码的运行结果如下。为什么同一块空间的值不一样?这是因为一开始ci是const类型变量,由于编译器认为ci的值不会被修改,因此会将ci的值预先加载到一块特定的寄存器中,后续每一访问ci时,不是通过访问虚拟地址空间,而是直接从cpu中的寄存器直接取值,后续ci的存储的值被修改为了20,但打印ci时,还是从寄存器中取因此会打印出10。本质是由编译器对const对象存取优化机制导致。想要禁止编译器做这个优化,就在const前面把volatile加上。
int main()
{
const int ci = 10;
int* pi = const_cast<int*>(&ci);
*pi = 20;
cout << *pi << endl;
cout << ci << endl;
}
假设类A和B具有继承关系,基类A的指针可以接受子类B的地址,当运行func函数时,如果传入的是B的地址则没事,但如果传入的是A的地址,则程序会崩溃,因为A内没有_b,这是一个越界访问的问题。在func这样的函数内,如何辨别pa指向的是基类对象的地址还是子类对象的地址呢?这时需要用到dynamic_cast。用法如下。如果pa指向的是子类对象,则转换成功,如果pa指向的是父类对象则转换失败返回nullptr。注意,dynamic_cast的应用只能针对继承中的多态类型(父类必须包含虚函数),dynamic_cast之所以能够识别父类的指针是指向父类对象还是子类对象,是通过对象的虚表的上方存储的标识信息来确定的。
class A
{
public:
virtual void f()
{}
int _a;
};
class B :public A
{
public:
int _b;
};
void func(A* pa)
{
B* pb = (B*)pa;
pb->_a = 1;
pb->_b = 2;
}
void func(A* pa)
{
B* pb = dynamic_cast<B*>(pa);
if (pb)
{
pb->_a = 1;
pb->_b = 2;
}
}
在类的构造函数前加explicit关键字可以防止构造时发生隐式类型转换。