c++ 命名的强制类型转换
显式转换:显式将一种类型转换为另一种类型。
References:
与命名的强制类型转换相比,旧式的强制类型转换从表现形式上来说不那么清晰明了,容易被看漏,所以一旦转换过程出现问题,追踪起来也更加困难。
C++ 引入新的强制类型转换机制,主要是为了克服C语言强制类型转换的以下三个缺点:
- 没有从形式上体现转换功能和风险的不同。
例如,将 int 强制转换成 double 是没有风险的,而将常量指针转换成非常量指针,将基类指针转换成派生类指针都是高风险的,而且后两者带来的风险不同(即可能引发不同种类的错误),C语言的强制类型转换形式对这些不同并不加以区分。
-
将多态基类指针转换成派生类指针时不检查安全性,即无法判断转换后的指针是否确实指向一个派生类对象。
-
难以在程序中寻找到底什么地方进行了强制类型转换。
一、static_cast
是一种静态的转换,在编译期就能确定的转换,可以完成C语言中的强制类型转换中的大部分工作,但需要注意的是,它不能转换掉表达式的 const、volitale 或者 __unaligned 属性。
- 将较大的算术类型转换为较小的算术类型
double slope = static_cast<double>(j)/i;
相当于 (double)j/i
#include <iostream>
using namespace std;
class A
{
public:
operator int() { return 1; }
operator char*() { return NULL; }
};
int main()
{
A a;
int n;
char* p = "New Dragon Inn";
n = static_cast <int> (3.14); // n 的值变为 3
n = static_cast <int> (a); //调用 a.operator int,n 的值变为 1
p = static_cast <char*> (a); //调用 a.operator char*,p 的值变为 NULL
n = static_cast <int> (p); //编译错误,static_cast不能将指针转换成整型
p = static_cast <char*> (n); //编译错误,static_cast 不能将整型转换成指针
return 0;
}
一般来说,如果编译器发现一个较大的算术类型试图赋值给较小的类型,就会给出警告信息;但是当我们执行了显式的类型转换后,警告信息就会被关闭了。
-
添加const: 将非const转换为const。
-
将表达式转换成void类型,并将转换后的结果丢弃
int val = 110;
static_cast<void>(val);
- static_cast对于编译器无法自动执行的类型转换也非常有用, 可以用于void*和其他指针类型之间的转换。例如,我们可以使用static_cast找回存在于void*指针中的值。
void* p = &d; //任何非常量对象的地址都能存入void*
double* dp = static_cast<double*>(p);
-
static_cast 不能用于在不同类型的指针之间(如char*与int*之间)互相转换,也不能用于整型和指针之间的互相转换,当然也不能用于不同类型的引用之间的转换。因为这些属于风险比较高的转换。
-
可以用于类继承结构中基类和派生类之间指针或引用的转换,向上转型安全,向下转型由于没有动态类型检查,是不安全的。
-
如果涉及左值到右值、数组到指针或函数到指针的转换,也可以通过static_cast显式执行
std::move()的实现
二、dynamic_cast
适用于以下情况:我们想使用基类对象的指针或引用执行某个派生类操作并且该操作不是虚函数。
相比于 static_cast 的编译时转换, dynamic_cast 的转换还会在运行时进行类型检查,转换的条件也比较苛刻,必须有继承关系的类之间才能转换,并且在基类中有虚函数才可以,有一种特殊的情况就是可以把类指针转换成 void* 类型。
不能用于基本类型。
- RTTI:
实现运行时类型识别。运行时类型识别(run-time type identification,RTTI)的功能由两个运算符实现:
- typeid运算符,用于返回表达式的类型。
- dynamic_cast运算符,用于将基类的指针或引用安全地转换成派生类的指针或引用。
-
dynamic_cast 用于在类的继承层次之间进行类型转换,它既允许向上转型(Upcasting),也允许向下转型(Downcasting)。 ** 可用于将基类的指针或引用安全地转换成派生类的指针或引用**。
-
有三种使用形式:
type通常应该是一个类类型,通常情况下应该含有虚函数。
e的类型必须符合以下三个条件中的任意一个:e的类型是目标type的公有派生类、e的类型是目标type的公有基类或者e的类型就是目标type的类型。如果符合,则类型转换可以成功。否则,转换失败。
- dynamic_cast<type*>(e) //e必须是一个有效的指针;
- dynamic_cast<type&>(e) //e必须是一个左值;
- dynamic_cast<type&&>(e) //e不能是左值;
- 在横向转换时指针发生了变化,可以看出 dynamic_cast 不是简单的数据强转,还进行了指针的偏移。
三、const_cast
将常量对象转换为非常量对象的行为:cast away the const. 实现有时需要对const对象进行修改。
-
const_cast 比较好理解,它用来去掉表达式的 const 修饰或 volatile 修饰。换句话说,const_cast 就是用来将 const/volatile 类型转换为非 const/volatile 类型。只有const_cast可以改变常量表达式的常量属性。
-
const_cast 不能去除变量的常量性,只能用来去除指向常数对象的指针或引用的常量性,且去除常量性的对象必须为指针或引用。
#include <iostream>
using namespace std;
int main(void) {
const int d = 2;
int* a = const_cast<int*>(&d);
*a = 3;
cout<<d<<endl;
return 0;
}
输出为:
2
这是因为使用变量d的语句在编译期就被替换为了2。
- 将 const 引用转换为同类型的非 const 引用,将 const 指针转换为同类型的非 const 指针时可以使用 const_cast 运算符。
#include <iostream>
using namespace std;
int main(void) {
int d = 2;
const int* a = &d;
int* b = const_cast<int*>(a);
cout<<*a<<endl;
cout<<*b<<endl;
*b = 3;
cout<<*a<<endl;
cout<<*b<<endl;
return 0;
}
输出:
2
2
3
3
四、 reinterpret_cast
- reinterpret_cast被用于不同类型指针或引用之间的转换,或者指针和整数之间的转换, 是对运算对象的二进制位的重新解释。
- 不同基础类型指针类型之间转换
int* ip;
char* pc = reinterpret_cast<char*>(ip);
我们必须牢记pc所指的真实对象是一个int而非字符,如果把pc当成普通的字符指针使用就可能在运行时发生错误。
-
基础类型指针与类对象指针之间的转换
-
将地址值转换成整数
- reinterpret_cast本质上依赖于机器。要想安全地使用reinterpret_cast必须对涉及的类型和编译器实现转换的过程都非常了解。
summary
- C/C++中不同数据类型进行运算或者赋值的时候会发生数据转换,这种转换有些是自动进行的,有些需要进行显示的强制类型转换
- 在C语言中强制类型转换写成(new_type_name) expression的形式,new_type_name 是要转换的目标类型,expression 是待转换的表达式
- 在C++中强制类型转换通过更明显的关键字来完成,分别是static_cast、 dynamic_cast, const_cast、 和 reinterpret_cast
- static_cast 是静态转换,在编译期完成完成转换,与C语言中的强制类型转换重合度最高
- dynamic_cast 是动态转换,在运行时转换会进行检查,必须用在有继承关系的多态结构中
- const_cast 是常量转换,用于取出指针或引用的常量属性,但是尽量通过设计杜绝它的使用场景
- reinterpret_cast 是一种内存数据的重新解释,比较原始,开发者使用它的时候应该明确的知道自己在做什么
————————————————
版权声明:本文为CSDN博主「AlbertS」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/albertsh/article/details/118663176