隐式类型转换(Implicit Type Conversion),又称自动类型转换,由编译器在编译阶段自动进行,能转就转,不能转就编译失败。
常见的隐式类型转换包括:
int
赋值转换为 double
。int
与 long
进行运算,会被提升为 long
。同时,超出类型的表示范围,会进行符号扩展或截断。true
,零值转换为 false
;对于指针类型,非空指针转换为 true
,空指针转换为 false
。void*
类型。数组名会被转换为指向数组第一个元素的指针,函数名会被转换为指向该函数的指针。void func(double d){} void test() { int i = 1; double d = i;//赋值运算符 double ret = i + d;//算术运算符 func(i);//函数调用 void* p = &i;//指针 }
显式类型转换(Explicit Type Conversion),又称为强制类型转换,需要程序员显式地指定转换方式。
格式:(type)expression
void test() { int i = 1; double d = (double)i; }
格式:static_cast
用于非多态类型的转换(静态转换),并且只适用相近类型之间的转换,对应C的隐式类型转换,例如基本数据类型之间的转换。
void test() { int i = 1; double d = static_cast(i);//静态转换,相近类型 int* pi = &i; void* p = static_cast(pi); }
格式:dynamic_cast
用于多态类型的转换(动态转换),并且用于向下转型:将一个父类对象的指针/引用转换为子类对象的指针或引用。
ps:向上转型,没有类型转换,属于赋值兼容规则。
class A { public: virtual void f(){} int _a = 1; }; class B :public A { public: virtual void f(){} int _b = 2; }; void test() { B b; A a = b; A& ra = b;//此处没有隐式类型转换 double d = 3.14; const int& ri = d;//此处有隐式类型转换 }
由于类型转换后的临时对象具有常性,所以要用常引用。而向上转型(切片)不用常引用,所以没有发生类型转换。
dynamic_cast 在运行时检查转换是否有效。如果转换无效,则指针类型返回空指针 nullptr
,引用类型抛出异常 std::bad_cast
。
void func(A* pa) { //B* p = (B*)pa;//直接向下转换,如果指向父类,则存在越界风险 B* p = dynamic_cast(pa); if (p) { p->_a++; p->_b++; } else { cout << "转换失败" << endl; } } void test() { B b; A a = b; func(&a); func(&b); }
直接向下转换,如果指向父类,则存在越界风险。运用 dynamic_cast 则可安全地进行向下转型。
格式:const_cast
用于修改对象的常量属性,可添加或移除变量的 const
或 volatile
属性。
void test() { const int a = 2; int* p = const_cast(&a); *p = 3; cout << a << endl; cout << *p << endl; cout << &a << endl; cout << p << endl; }
通过打印可以发现,a的值依旧为2,而*p的值为3,并且它们的地址相同。这是为什么呢?其实,编译器在这里做了优化,常变量a会被放进寄存器或者直接替换。
这时我们可以加上 volatile
关键字,其作用是阻止编译器对标记的变量进行某些优化。
void test() { volatile const int a = 2; int* p = const_cast(&a); *p = 3; cout << a << endl; cout << *p << endl; //cout << &a << endl; printf("%p\n", &a); cout << p << endl; }
此时还有一个问题,那就是用 volatile
修饰的变量类型无法和参数为 void*
的 operator<<
函数匹配,从而会匹配到不合适的重载函数,输出错误的地址值。所以,此处我们用 printf 进行打印即可。
格式:reinterpret_cast
为操作数的位模式提供较低层次的重新解释。用于不相近类型之间的转换,对应C的显式类型转换。
void test() { int i = 1; int* p = &i; //double* p1 = static_cast(i);//error double* p2 = reinterpret_cast(i); //double* p3 = static_cast(p);//error double* p4 = reinterpret_cast(p); }
RTTI
(Run-Time Type Identification)是C++语言中的一个特性,它允许程序在运行时确定对象的实际类型。
用于打印变量的类型。
void test() { auto f = [](int x, int y){return x + y;}; cout << typeid(f).name() << endl; }
class Base {}; class Derive :public Base {}; void func(Base* pb) { Derive* pd = dynamic_cast(pb); }
可将变量的类型提取,作为参数传递。
void test() { int x = 1; double y = 2.0; auto ret = x * y; vector v; }
C风格的类型转换,将不同类型的转换混在一起,不够清晰,可视性差,难以跟踪错误的转换。所以我们推荐使用C++的类型转换,加强类型转换的可视性,提高代码的可读性与可维护性。
一般来说:
static_cast
进行相近类型的转换。dynamic_cast
在运行时进行多态类型检查。const_cast
移除或添加常量性。reinterpret_cast
进行不相近类型的转换。