C++语法系列之类型转换
前言
类型转换是经常存在的情况,类型转换分为隐式类型转化 和 显式类型转化
隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译失败
double i = 3.3;
int b = i; //隐式类型转化 double -> int
C++搞出来了四种强制类型转换操作符,一一来介绍一下。
显式类型转化:需要用户自己处理
int *p = (int*)malloc(sizeof(int));//显示
转换当然是有问题的了
- 隐式类型转化有些情况下可能会出问题:比如数据精度丢失
- 显式类型转换将所有情况混合在一起,代码不够清晰
所以C++为了加强类型转换的可视性,引入了四种命名的强制类型转换操作符。下面来一一介绍一下。
四种指针的写法均为 xxx_cast< T >(i),必须加括号,有点像强制类型转换
一、static_cast
static_cast用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用static_cast,但它不能用于两个不相关的类型进行转换
就是说白了,你不加static_cast可以转换的或者显示类型转换的基本上加了这个也可以。类型不相关的不可以转换。
基本用法
double i = 5;
int j = static_cast<int>(i);
花里胡哨的用法:
class A {
public:explicit A(int x) {}
};
int main()
{A a(5);A a1 = static_cast<A>(5);A a2 = 5;//errorreturn 0;
}
加上explicit不允许实现隐式类型转化,但是可以玩static_cast
class Base
{};
class Derived : public Base
{
public:int _a;
};int main()
{Base* base = new Derived();Derived* derived2 = static_cast<Derived*>(base); // 可行cout << derived2->_a << endl;return 0;
}
这样可以把父类的指针给子类,但是会有潜在的问题,子类成员无法初始化,所以这样别用,用后面的dynamic_cast
二、reinterpret_cast
有点与static_cast相反,reinterpret_cast操作符通常为操作数的位模式提供较低层次的重新解释,用于将一种类型转换为另一种不同的类型
int main()
{int d = 4;int* p = reinterpret_cast<int*>(d);int* pd = &d;int address = reinterpret_cast<int>(pd);return 0;
}
三、const_cast
先来看一段代码:
int main()
{const int a = 10;int* p = (int*)& a;(*p)++;cout << a << endl;cout << *p << endl;return 0;
}
这样是不是很奇怪啊,实际上因为a是const属性,编译器知道他是const,在输出*a的时候会直接取出来这个10,所以这里先介绍一个关键字 volatile,这个意思就告诉编译器他是不稳定的
加上volatile这句话:
但是即使加上你想直接赋值也是不可以的
volatile const int a = 10;
a = 5;//error
进而引出了const_cast的作用:
const_cast最常用的用途就是删除变量的const属性,方便赋值
int main()
{const int a = 5;int* p = const_cast<int*>(&a);*p = 4;cout << a << endl;return 0;
}
但是这样还没变。。。。所以还需要加上volatile,所以const_cast只是去掉他的属性,但是需要修改还要进一步操作(最好就别修改)
那他的作用就没有了?当然不是:
在调用一些遗留的非const接口时,若你手上只有const对象,就可以使用const_cast。示例如下:
void func(int* ptr) {// 一些非const操作*ptr += 5;
}int main() {int p = 10;const int* constPtr = &p;// func(constPtr); // errorfunc(const_cast<int*>(constPtr)); // 可行return 0;
}
实现代码的复用:
在一个类中,const和非const版本的成员函数可能有大量重复的代码。这时可以让其中一个函数调用另一个,从而减少代码冗余。
所以,const_cast主要用于在特定场景下移除对象的const或volatile属性,但在使用时必须谨慎,要确保不会对真正的常量对象进行修改,以避免引发未定义行为,
四、dynamic_cast
学过DP的应该知道dynamic是啥,dynamic意思是动态,主要用于具有继承关系的父类和子类之间的转化。
dynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针或引用
向上转型:子类对象指针/引用->父类指针/引用(不需要转换,赋值兼容规则)(切片)
向下转型:父类对象指针/引用->子类指针/引用(用dynamic_cast转型是安全的)
注意:
- dynamic_cast只能用于父类含有虚函数的类(如果不含虚函数就会报错)
- dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0,与指针不同,引用不能为 nullptr,因此转换失败时会抛出异常。
class A
{
public :
virtual void f(){}
};
class B : public A
{};
void fun (A* pa)
{
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;
}
五、RTTI(Run-Time Type Identification)
RTTI 的基本概念
运行时类型识别:允许程序在运行时查询和识别对象的实际类型,而不仅仅依赖于编译时的类型信息。
作用:主要用于解决面向对象编程中的多态性问题,特别是在需要将基类指针或引用转换为派生类类型时。
C++主要通过typeid和dynamic_cast实现。
typeid:返回一个表示对象类型的type_info对象,可以用于比较类型。
dynamic_cast:用于安全地将基类指针或引用转换为派生类类型,失败时返回nullptr(指针)或抛出异常(引用)。
decltype:用来识别类型
总结
虽然这里讲了几种方法,但是实际应用中需要小心的使用转换,建议不要使用,或者在保证安全的情况下使用