C++中的数据类型强制转换⽅法
1. c强制转换与c++强制转换
c语⾔强制类型转换主要⽤于基础的数据类型间的转换,语法为:
(type-id)expression//转换格式1
type-id(expression)//转换格式2
c++除了能使⽤c语⾔的强制类型转换外,还新增了四种强制类型转换:static_cast、dynamic_cast、const_cast、reinterpret_cast,主要运⽤于继承关系类间的强制转化,语法为:
static_cast<new_type> (expression)
dynamic_cast<new_type> (expression)
const_cast<new_type> (expression)
reinterpret_cast<new_type> (expression)
备注:new_type为⽬标数据类型,expression为原始数据类型变量或者表达式。
《Effective C++》中将c语⾔强制类型转换称为旧式转型,c++强制类型转换称为新式转型。
2. static_cast、dynamic_cast、const_cast、reinterpret_cast
static_cast
static_cast相当于传统的C语⾔⾥的强制转换,该运算符把expression转换为new_type类型,⽤来强迫隐式转换,例如non-const对象转为const对象,编译时检查,⽤于⾮多态的转换,可以转换指针及其他,但没有运⾏时类型检查来保证转换的安全性。它主要有如下⼏种⽤法:
①⽤于类层次结构中基类(⽗类)和派⽣类(⼦类)之间指针或引⽤的转换。
进⾏上⾏转换(把派⽣类的指针或引⽤转换成基类表⽰)是安全的;
进⾏下⾏转换(把基类指针或引⽤转换成派⽣类表⽰)时,由于没有动态类型检查,所以是不安全的。
②⽤于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发⼈员来保证。
③把空指针转换成⽬标类型的空指针。
④把任何类型的表达式转换成void类型。
注意:static_cast不能转换掉expression的const、volatile、或者__unaligned属性。
基本类型数据转换举例如下:
char a = 'a';
int b = static_cast<char>(a);//正确,将char型数据转换成int型数据
double *c = new double;
void *d = static_cast<void*>(c);//正确,将double指针转换成void指针
int e = 10;
const int f = static_cast<const int>(e);//正确,将int型数据转换成const int型数据
const int g = 20;
int *h = static_cast<int*>(&g);//编译错误,static_cast不能转换掉g的const属性
类上⾏和下⾏转换:
if(Derived *dp = static_cast<Derived *>(bp)){//下⾏转换是不安全的
//使⽤dp指向的Derived对象
}
else{
//使⽤bp指向的Base对象
}
if(Base*bp = static_cast<Derived *>(dp)){//上⾏转换是安全的
//使⽤bp指向的Derived对象
}
else{
/
/使⽤dp指向的Base对象
}
dynamic_cast
dynamic_cast<type*>(e)
dynamic_cast<type&>(e)
dynamic_cast<type&&>(e)
type必须是⼀个类类型,在第⼀种形式中,type必须是⼀个有效的指针,在第⼆种形式中,type必须是⼀个左值,在第三种形式
中,type必须是⼀个右值。在上⾯所有形式中,e的类型必须符合以下三个条件中的任何⼀个:e的类型是是⽬标类型type的公有派⽣类、e 的类型是⽬标type的共有基类或者e的类型就是⽬标type的的类型。如果⼀条dynamic_cast语句的转换⽬标是指针类型并且失败了,则结果为0。如果转换⽬标是引⽤类型并且失败了,则dynamic_cast运算符将抛出⼀个std::bad_cast异常(该异常定义在typeinfo标准库头⽂件中)。e 也可以是⼀个空指针,结果是所需类型的空指针。
dynamic_cast主要⽤于类层次间的上⾏转换和下⾏转换,还可以⽤于类之间的交叉转换(cross cast)。
在类层次间进⾏上⾏转换时,dynamic_cast和static_cast的效果是⼀样的;
在进⾏下⾏转换时,dynamic_cast具有类型检查的功能,⽐static_cast更安全。dynamic_cast是唯⼀⽆法由旧式语法执⾏的动作,也是唯⼀可能耗费重⼤运⾏成本的转型动作。
(1)指针类型
举例,Base为包含⾄少⼀个虚函数的基类,Derived是Base的共有派⽣类,如果有⼀个指向Base的指针bp,我们可以在运⾏时将它转换成指向Derived的指针,代码如下:
if(Derived *dp = dynamic_cast<Derived *>(bp)){
//使⽤dp指向的Derived对象
}
else{
/
/使⽤bp指向的Base对象
}
值得注意的是,在上述代码中,if语句中定义了dp,这样做的好处是可以在⼀个操作中同时完成类型转换和条件检查两项任务。
(2)引⽤类型
因为不存在所谓空引⽤,所以引⽤类型的dynamic_cast转换与指针类型不同,在引⽤转换失败时,会抛出std::bad_cast异常,该异常定义在头⽂件typeinfo中。
void f(const Base &b){
try{
const Derived &d = dynamic_cast<const Base &>(b);
//使⽤b引⽤的Derived对象
}
catch(std::bad_cast){
//处理类型转换失败的情况
}
}
const_cast
const int* p; //p可变,p指向的内容不可变
int const* p; //p可变,p指向的内容不可变
int* const p; //p不可变,p指向的内容可变
const修饰函数返回值表⽰返回值不可改变,多⽤于返回指针的情形
volatile可理解为编译器警告指⽰字
volatile⽤于告诉编译器必须每次去内存中取变量值
volatile主要修饰可能被多个线程访问的变量
volatile也可以修饰可能被未知因数更改的变量
1、常量指针被转化成⾮常量的指针,并且仍然指向原来的对象;
2、常量引⽤被转换成⾮常量的引⽤,并且仍然指向原来的对象;
3、const_cast⼀般⽤于修改指针。如const char *p形式;
未定义⾏为:C++标准对此类⾏为没有做出明确规定.同⼀份代码在使⽤不同的编译器会有不同的效果.在 vs2017 下, 虽然代码中c_val ,
use_val , ptr_val看到的地址是⼀样的.但是c_val的值并没有改变.有可能在某种编译器实现后,这⼀份代码的c_val会被改变.也有可能编译器对这类⾏为直接error或warning.
const_cast,⽤于修改类型的const或volatile属性。
该运算符⽤来修改类型的const(唯⼀有此能⼒的C++-style转型操作符)或volatile属性。除了const 或volatile修饰之外, new_type和expression的类型是⼀样的。
①常量指针被转化成⾮常量的指针,并且仍然指向原来的对象;
②常量引⽤被转换成⾮常量的引⽤,并且仍然指向原来的对象;
③const_cast⼀般⽤于修改底指针。如const char *p形式。
举例转换如下:
const int g = 20;
int *h = const_cast<int*>(&g);//去掉const常量const属性
const int g = 20;
int &h = const_cast<int &>(g);//去掉const引⽤const属性
const char *g = "hello";
char *h = const_cast<char *>(g);//去掉const指针const属性
reinterpret_cast
typec转dp
new_type必须是⼀个指针、引⽤、算术类型、函数指针或者成员指针。它可以把⼀个指针转换成⼀个整数,也可以把⼀个整数转换成⼀个指针(先把⼀个指针转换成⼀个整数,再把该整数转换成原类型的指针,还可以得到原先的指针值)。
reinterpret_cast意图执⾏低级转型,实际动作(及结果)可能取决于编辑器,这也就表⽰它不可移植。
举⼀个错误使⽤reintepret_cast例⼦,将整数类型转换成函数指针后,vc++在执⾏过程中会报"...中的 0xxxxxxxxx 处有未经处理的异常: 0xC0000005: Access violation"错误:
#include <iostream>
using namespace std;
int output(int p){
cout << p <<endl;
return 0;
}
typedef int (*test_func)(int );//定义函数指针test_func
int main(){
int p = 10;
test_func fun1 = output;
fun1(p);//正确
test_func fun2 = reinterpret_cast<test_func>(&p);
fun2(p);//...处有未经处理的异常: 0xC0000005: Access violation
return 0;
}
、C++之⽗和也都指出:错误的使⽤reinterpret_cast很容易导致程序的不安全,只有将转换后的类型值转换回到其原始类型,这样才是正确使⽤reinterpret_cast⽅式。
MSDN中也提到了,实际中可将reinterpret_cast应⽤到哈希函数中,如下(64位系统中需将unsigned int修改为unsigned long):
// expre_reinterpret_cast_Operator.cpp
// compile with: /EHsc
#include <iostream>
// Returns a hash code based on an address
unsigned short Hash( void *p ) {
unsigned int val = reinterpret_cast<unsigned int>( p );
return ( unsigned short )( val ^ (val >> 16));
}
using namespace std;
int main() {
int a[20];
for ( int i = 0; i < 20; i++ )
cout << Hash( a + i ) << endl;
}
另外,static_cast和reinterpret_cast的区别主要在于多重继承,⽐如
class A {
public:
int m_a;
};
class B {
public:
int m_b;
};
class C : public A, public B {};
那么对于以下代码:
C c;
printf("%p, %p, %p", &c, reinterpret_cast<B*>(&c), static_cast <B*>(&c));
前两个的输出值是相同的,最后⼀个则会在原基础上偏移4个字节,这是因为static_cast计算了⽗⼦类指针转换的偏移量,并将之转换到正确的地址(c⾥⾯有m_a,m_b,转换为B*指针后指到m_b处),⽽reinterpret_cast却不会做这⼀层转换。
因此, 你需要谨慎使⽤ reinterpret_cast。
3. c++强制转换注意事项
新式转换较旧式转换更受欢迎。原因有⼆,⼀是新式转型较易辨别,能简化“出类型系统在哪个地⽅被
破坏”的过程;⼆是各转型动作的⽬标愈窄化,编译器愈能诊断出错误的运⽤。
尽量少使⽤转型操作,尤其是dynamic_cast,耗时较⾼,会导致性能的下降,尽量使⽤其他⽅法替代。
error: invalid static_cast from type ‘xxx*’ to type ‘yyy*’
通常来说, static_cast 不适⽤于不同指针类型之间的转换, 因为它们是完全不同的两个类型. 如果
⾃⼰清楚⾃⼰在做什么, 通常对于不同指针类型的转换应该使⽤reinterpret_cast
————————————————参考
blog.csdn/u010540025/article/details/81231495
wwwblogs/chenyangchun/p/6795923.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论