一、C语言中的类型转换
在C语言中,如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与接收返回值类型不一致时,就需要发生类型转化,C语言中总共有两种形式的类型转换:隐式类型转换和显式类型转换。
- 隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译失败,注意:只有相近类型之间才能发生隐式类型转换
- 显式类型转化:需要用户自己处理
例如:比如int
和double
表示的都是数值,只不过它们表示的范围和精度不同。而指针类型表示的是地址编号,因此整型和指针类型之间不会进行隐式类型转换,如果需要转换则只能进行显式类型转换。
void Test() { 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); }
C语言类型转换的优缺点::
C风格的转换格式很简单,但还是有不少缺点的:
- 隐式类型转化有些情况下可能会出问题:比如数据精度丢失
- 显式类型转换将所有情况混合在一起,代码不够清晰
C语言转换的可视性比较差,相近类型之间发生隐式类型转换,难以跟踪错误的转换。
因此C++
为了加强类型转换的可视性,提出了自己的类型转化风格,因为C++
要兼容C
语言,所以C++
中还可以使用C
语言的转化风格。
二、C++的类型转换
标准C++为了加强类型转换的可视性,引入了四种命名的强制类型转换操作符:
static_cast
、reinterpret_cast
、const_cast
、dynamic_cast
1、static_cast
static_cast
用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用static_cast
,但它不能用于两个不相关的类型进行转换。
int main() { double d = 10.21; int i = static_cast<int> (d); cout << i << " " << d << endl; // 转换失败 //int* p = static_cast<int*> (i); //cout << p << endl; return 0; }
2、reinterpret_cast
reinterpret_cast
操作符通常为重新解释,用于将一种类型转换为另一种不同的类型。
int main() { // 转换失败 //double d = 10.21; //int i = reinterpret_cast<int> (d); //cout << i << " " << d << endl; // 转换成功 int a = 10; int* p = reinterpret_cast<int*> (a); cout << p << endl; return 0; }
3、const_cast
const_cast
最常用的用途就是删除变量的const
属性,方便赋值,注意const_cast<>
<>里面必须是指针或者引用类型。
int main() { const int a = 10; int* pa = const_cast<int*> (&a); *pa = 20; cout << " a :" << a << endl; cout << "*pa :" << *pa << endl; return 0; }
可能看到这个结果你会很不解,明明我们都已经修改了a,为什么打印的结果还是10?
解释一下:
- 由于编译器认为
const
修饰的变量是不会被修改的,因此会将const
修饰的变量存放到寄存器当中,当需要读取const
变量时就会直接从寄存器中进行读取,而我们修改的实际上是内存中的a
的值,因此最终打印出a
的值是未修改之前的值。 - 如果不想让编译器将const变量优化到寄存器当中,可以用
volatile
关键字对const变量进行修饰,这时当要读取这个const变量时编译器就会从内存中进行读取,即保持了该变量在内存中的可见性。
int main() { volatile const int a = 10; int* pa = const_cast<int*> (&a); *pa = 20; cout << " a :" << a << endl; cout << "*pa :" << *pa << endl; return 0; }
4、dynamic_cast
dynamic_cast
用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)
- 向上转型:子类对象指针/引用->父类指针/引用(不需要转换,赋值兼容规则)
- 向下转型:父类对象指针/引用->子类指针/引用(用dynamic_cast转型是安全的)
而向下转型分为两种情况:
- 如果父类的指针(或引用)指向的是一个父类对象,那么将其转换为子类的指针(或引用)是不安全的,因为转换后可能会访问到子类的资源,而这个资源是父类对象所没有的。
- 如果父类的指针(或引用)指向的是一个子类对象,那么将其转换为子类的指针(或引用)则是安全的。
使用C风格的强制类型转换进行向下转型是不安全的,因为此时无论父类的指针(或引用)指向的是父类对象还是子类对象都会进行转换。
而使用dynamic_cast
进行向下转型则是安全的。
- 如果父类的指针(或引用)指向的是子类对象那么
dynamic_cast
会转换成功。 - 如果父类的指针(或引用)指向的是父类对象那么
dynamic_cast
会转换失败并返回一个空指针。
class A { public: virtual void f() {} private: int _a; }; class B : public A { private: int _b; }; int main() { A aa; B bb; A* pa = &aa; A* pb = &bb; cout << "强制类型转换, 父转子pa:" << (B*)pa << endl; cout << "强制类型转换, 父转子pb:" << (B*)pb << endl; cout << "---------------------------------------" << endl; cout << "dynamic_cast类型转换, 父转子pb:" << dynamic_cast<B*>(pa) << endl; cout << "dynamic_cast类型转换, 父转子pb:" << dynamic_cast<B*>(pb) << endl; }
注意:
dynamic_cas
t只能用于父类含有虚函数的类dynamic_cast
会先检查是否能转换成功,能成功则转换,不能则返回0
三、RTTI
RTTI:Run-time Type identification的简称,即:运行时类型识别。
C++通过以下方式来支持RTTI:
typeid
运算符dynamic_cast
运算符decltype
4中类型转化的应用场景:
static_cast
用于相近类型的类型之间的转换,编译器隐式执行的任何类型转换都可用static_cast
。reinterpret_cast
用于两个不相关类型之间的转换。const_cast
用于删除变量的const属性,方便赋值。dynamic_cast
用于安全的将父类的指针(或引用)转换成子类的指针(或引用)。