C++ 强制转换的四种cast操作符

C风格(C-style)强制转型如下:

(T) expression 或 T(expression) //函数风格(Function-style)

两种形式之间没有本质上的不同。

ANSI-C++标准定义的四个cast操作符

对于转换简单类型而言C风格转型工作得很好。然而,这样的转换符也能不分皂白地应用于类和类的指针。
ANSI-C++标准定义了四个新的转换符,目的在于控制类(class)之间的类型转换。

static_cast<new_type> (expression)
dynamic_cast<new_type> (expression) 
const_cast<new_type> (expression) 
reinterpret_cast<new_type> (expression)
  1. const_cast

用法:const_cast< type-id > (expression)

用于修改类型的const或volatile属性。除了const或volatile修饰之外,type-id和expression的类型是一样的,一般用于强制消除对象的常量性。它是唯一能做到这一点的 C++ 风格的强制转型,而C不提供消除const的机制。

常量指针被转化成非常量指针,并且仍然指向原来的对象;常量引用被转换成非常量引用,并且仍然指向原来的对象;常量对象被转换成非常量对象。

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属性
  1. static_cast

用法:static_cast < type-id > (expression)

该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性。它允许执行任意的隐式转换和相反转换动作。主要有如下几种用法:
1)用于基本数据类型之间的转换,如把int转换成char,int转换成enum,non-const对象转型为const对象(这里相反方向不可以,C++只有const_cast可以),这种转换的安全性也要开发人员来保证。
2)把空指针转换成目标类型的指针(之前的做法是用强制转换(type-id *) )。
3)把任何类型的表达式转换成void类型。
4)在类的指针上,允许子类类型的指针转换为父类类型的指针(upercasting这是一个有效的隐式转换);也能够执行相反动作,即转换父类为它的子类(downcasting),这种转换的安全性需要开发人员来保证。进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;
进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的。

class Base {};
class Derived : public Base {};

Base *a = new Base;
Derived *b = NULL;

b = static_cast<Derived *>(a); //可以通过编译,但存在安全隐患(如访问Derived的成员),此处区别父类指针指向子类对象

注意:
1)static_cast不能转换掉expression的const、volitale、或者__unaligned属性。
2)在非基本类型或上下转型中,被转换的父类需要检查是否与目的类型相一致,否则,如果在两个完全不相干的类之间进行转换,将会导致编译出错。

  1. reinterpret_cast

用法: reinterpret_cast< type-id > (expression)

type-id必须是一个指针、引用、算术类型、函数指针或者成员指针。它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针。

这个操作符能够在非相关的类型之间转换。操作结果只是简单的从一个指针到别的指针的值的二进制拷贝。在类型之间指向的内容不做任何类型的检查和转换。

reinterpret_cast通常为操作数的位模式提供较低层的重新解释,故用于底层的强制转型,从而实现依赖,不可移植。

错误的使用reinterpret_cast很容易导致程序的不安全,只有将转换后的类型值转换回到其原始类型,这样才是正确使用reinterpret_cast方式。

int n = 9;
double d = reinterpret_cast<double &> (n); // reinterpret_cast 仅仅是复制n的比特位到d,因此d包含无用值。

另外,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个字节

前两个的输出值是相同的,最后一个则会在原基础上偏移4个字节,这是因为static_cast计算了父类和子类指针转换的偏移量,并将之转换到正确的地址(c里面有m_a, m_b,转换为B*指针后指到m_b处),而reinterpret_cast却不会做这一层转换。因此,你需要谨慎使用reinterpret_cast。

  1. dynamic_cast

只用于对象的指针和引用,主要用于执行“安全的向下转型”,也就是说,要确定一个对象是否是一个继承体系中的一个特定类型。它是唯一不能用旧风格语法执行的强制转型,也是唯一可能有重大运行时代价的强制转型。

当用于多态类型时(包含虚函数),它允许任意的隐式类型转换以及相反过程。不过与static_cast不同,在隐式转换的相反过程值中,dynamic_cast根据RTTI信息检查操作是否有效,即在转换时dynamic_cast会检查转换是否能返回一个被请求的有效的完整对象。这种检查不是语法上的,而是真实情况的检查,因此属于耗费重大运行成本的转型动作。检测在运行时进行,如果被转换的指针不是一个被请求的有效完整的对象指针,返回值为NULL。

先看RTTI相关部分,通常许多编译器都是通过vtable找到对象的RTTI信息的,这也就意味着,如果基类没有虚函数,也就无法判断一个基类指针变量所指对象的真实类型,这时候dynamic_cast只能用来做安全的转换(upercasting),如从派生类指针转换成基类指针,而这种转换其实并不需要dynamic_cast参与。

class Base { virtual dummy() {} };
class Derived : public Base {};
class Other{} ;

Base* b1 = new Derived; //父类指针指向子类对象
Base* b2 = new Base;

Derived* d1 = dynamic_cast<Derived *>(b1);  // true
Derived* d2 = dynamic_cast<Derived *>(b2);  // false : returns 'NULL'

//如果一个引用类型执行了类型转换并且这个转换是不可能的,运行时会抛出bad_cast异常
Derived d3 = dynamic_cast<Derived &>(*b1);  // true
Derived d4 = dynamic_cast<Derived &>(*b2);  // false : exception thrown

注意:Base需要有虚函数,否则会编译出错。

小结

四种类型转换操作符对于隐式的类型转换没有必要。

static_cast在更宽上范围内可以完成映射,这种不加限制的映射伴随着不安全性。

在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;在进行下行转换时(基类需要包含虚函数),dynamic_cast具有类型检查的功能,牺牲了效率,但比static_cast安全。

参考:https://blog.csdn.net/starryheavens/article/details/4617637

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值