static_cast, dynamic_cast, const_cast探讨【转】

 首先回顾一下C++类型转换:

C++类型转换分为:隐式类型转换和显式类型转换

第1部分. 隐式类型转换


又称为“标准转换”,包括以下几种情况:
1) 算术转换(Arithmetic conversion) : 在混合类型的算术表达式中, 最宽的数据类型成为目标转换类型。

 

int ival = 3;
double dval = 3.14159;

ival 
+ dval;//ival被提升为double类型

2)一种类型表达式赋值给另一种类型的对象:目标类型是被赋值对象的类型

int *pi = 0// 0被转化为int *类型
ival = dval; // double->int

例外:void指针赋值给其他指定类型指针时,不存在标准转换,编译出错

3)将一个表达式作为实参传递给函数调用,此时形参和实参类型不一致:目标转换类型为形参的类型

extern double sqrt(double);

cout 
<< "The square root of 2 is " << sqrt(2<< endl;
//2被提升为double类型:2.0

4)从一个函数返回一个表达式,表达式类型与返回类型不一致:目标转换类型为函数的返回类型

double difference(int ival1, int ival2)
{
    
return ival1 - ival2;
    
//返回值被提升为double类型
}


第2部分. 显式类型转换


被称为“强制类型转换”(cast)
C     风格: (type-id)
C++风格: static_castdynamic_castreinterpret_cast、和const_cast..

 

关于强制类型转换的问题,很多书都讨论过,写的最详细的是C++ 之父的《C++ 的设计和演化》。最好的解决方法就是不要使用C风格的强制类型转换,而是使用标准C++的类型转换符:static_cast, dynamic_cast。标准C++中有四个类型转换符:static_castdynamic_castreinterpret_cast、和const_cast。下面对它们一一进行介绍。

static_cast


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


说明:该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性。

来源:为什么需要static_cast强制转换?
情况1:void指针->其他类型指针
情况2:改变通常的标准转换
情况3:避免出现可能多种转换的歧义



它主要有如下几种用法:
  • 用于类层次结构中基类和子类之间指针或引用的转换。进行上行转换(把子类的指针或引用转换成基类表示)是安全的;进行下行转换(把基类指针或引用转换成子类指针或引用)时,由于没有动态类型检查,所以是不安全的。
  • 用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。
  • 把void指针转换成目标类型的指针(不安全!!)
  • 把任何类型的表达式转换成void类型。
注意:static_cast不能转换掉expression的const、volitale、或者__unaligned属性。

dynamic_cast


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

说 明:该运算符把expression转换成type-id类型的对象。Type-id必须是类的指针、类的引用或者void *;如果type-id是类指针类型,那么expression也必须是一个指针,如果type-id是一个引用,那么expression也必须是一个 引用。

来源:为什么需要dynamic_cast强制转换?
简单的说,当无法使用virtual函数的时候

典型案例:
Wicrosoft公司提供给我们一个类库,其中提供一个类Employee.以头文件Eemployee.h和类库.lib分发给用户
显然我们并无法得到类的实现的源代码
//Emplyee.h
class Employee 
{
public:
    
virtual int salary();
};

class Manager : public Employee
{
public
    
int salary();
};

class Programmer : public Employee
{
public:
    
int salary();
};

我们公司在开发的时候建立有如下类:
class MyCompany
{
public:
    
void payroll(Employee *pe);
    
//
};

void MyCompany::payroll(Employee *pe)
{
    
//do something
}

但是开发到后期,我们希望能增加一个bonus()的成员函数到W$公司提供的类层次中。
假设我们知道源代码的情况下,很简单,增加虚函数:
//Emplyee.h
class Employee 
{
public:
    
virtual int salary();
    
virtual int bonus();
};

class Manager : public Employee
{
public
    
int salary();
};

class Programmer : public Employee
{
public:
    
int salary();
    
int bonus();
};

//Emplyee.cpp

int Programmer::bonus()
{
    
//
}
payroll()通过多态来调用bonus()
class MyCompany
{
public:
    
void payroll(Employee *pe);
    
//
};

void MyCompany::payroll(Employee *pe)
{
    
//do something
    //pe->bonus();
}

但是现在情况是,我们并不能修改源代码,怎么办?dynamic_cast华丽登场了!
在Employee.h中增加bonus()声明,在另一个地方定义此函数,修改调用函数payroll().重新编译,ok
//Emplyee.h
class Employee 
{
public:
    
virtual int salary();
};

class Manager : public Employee
{
public
    
int salary();
};

class Programmer : public Employee
{
public:
    
int salary();
    
int bonus();//直接在这里扩展
};

//somewhere.cpp

int Programmer::bonus()
{
    
//define
}

class MyCompany
{
public:
    
void payroll(Employee *pe);
    
//
};

void MyCompany::payroll(Employee *pe)
{
    Programmer 
*pm = dynamic_cast<Programmer *>(pe);
    
    
//如果pe实际指向一个Programmer对象,dynamic_cast成功,并且开始指向Programmer对象起始处
    if(pm)
    {
        
//call Programmer::bonus()
    }
    //如果pe不是实际指向Programmer对象,dynamic_cast失败,并且pm = 0
    
else
    {
        
//use Employee member functions
    }
}


dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。

在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。
class Base
{
public:
    
int m_iNum;
    
virtual void foo();
};

class Derived:public Base
{
public:
    
char *m_szName[100];
};

void func(Base *pb)
{
    Derived 
*pd1 = static_cast<Derived *>(pb);

    Derived 
*pd2 = dynamic_cast<Derived *>(pb);
}

在上面的代码段中,
如果pb实际指向一个Derived类型的对象,pd1和pd2是一样的,并且对这两个指针执行Derived类型的任何操作都是安全的;
如果pb实际指向的是一个Base类型的对象,那么pd1将是一个指向该对象的指针,对它进行Derived类型的操作将是不安全的(如访问m_szName),而pd2将是一个空指针(即0,因为dynamic_cast失败)。
另 外要注意:Base要有虚函数,否则会编译出错;static_cast则没有这个限制。这是由于运行时类型检查需要运行时类型信息,而这个信息存储在类 的虚函数表(关于虚函数表的概念,详细可见<Inside c++ object model>)中,只有定义了虚函数的类才有虚函数表,没有定义虚函数的类是没有虚函数表的。

另外,dynamic_cast还支持交叉转换(cross cast)。如下代码所示。
class Base
{
public:
    
int m_iNum;
    
virtual void f(){}
};



class Derived1 : public Base
{

};

class Derived2 : public Base
{

};

void foo()
{
    derived1 
*pd1 = new Drived1;

    pd1
->m_iNum = 100;

    Derived2 
*pd2 = static_cast<Derived2 *>(pd1); //compile error

    Derived2 
*pd2 = dynamic_cast<Derived2 *>(pd1); //pd2 is NULL

    delete pd1;
}

在函数foo中,使用static_cast进行转换是不被允许的,将在编译时出错;而使用 dynamic_cast的转换则是允许的,结果是空指针。

reinpreter_cast


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


说明:type-id必须是一个指针、引用、算术类型、函数指针或者成员指针。它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原先的指针值)。


该运算符的用法比较多。

const_cast

用法:const_cast<type_id> (expression)

说明:该运算符用来修改类型的const或volatile属性。除了const 或volatile修饰之外, type_id和expression的类型是一样的。


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

Voiatile和const类试。举如下一例:
class B{

public:

int m_iNum;

}

void foo(){

const B b1;

b1.m_iNum = 100; //comile error

B b2 = const_cast<B>(b1);

b2. m_iNum = 200; //fine
}

上面的代码编译时会报错,因为b1是一个常量对象,不能对它进行改变;使用const_cast把它转换成一个常量对象,就可以对它的数据成员任意改变。注意:b1和b2是两个不同的对象。

转载于:https://www.cnblogs.com/candycloud/p/3624328.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/258810.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RANSAC算法注记

今天学习了一下RANSAC随机样本一致性算法&#xff0c;其在图像融合、特征点匹配方面有很强大的应用。网上已经有很多人写了关于这方面的文档&#xff0c;就不再造轮子了。特此罗列出来&#xff0c;以供后续参考。 我的数学之美&#xff08;一&#xff09;——RANSAC算法详解 …

PMP 第七章 项目成本管理

估算成本 制定预算 控制成本 1.成本管理计划的内容和目的是什么? 包括对成本进行估算 预算和控制的各过程&#xff0c;从而确保项目在批准的预算内完工。 2.直接成本、间接成本、可变成本、固定成本、质量成本的内容分别是什么?成本估算的工具有哪些? 成本估算工具 1…

您的请求参数与订单信息不一致_[淘客订单检测]淘宝客订单检测接口,淘客订单查询API...

功能1.输入交易的订单编号&#xff0c;即可查询该订单是否为淘宝客订单。有意向请联系卫星weixiaot168。2.查询结果 0:不是淘宝客订单&#xff1b;1:是。3.根据淘宝官方的后台数据&#xff0c;进行检测&#xff0c;数据真实且有效。4.有效防止佣金损失&#xff0c;降低商家补单…

DebugView输出调试信息

在写windows程序时&#xff0c;需要输出一些调试信息&#xff0c;这里介绍一种极其方便的方法。即使用OutputDebugString 在Debug模式下输出调试信息&#xff0c;在Release模式下不输出。 我们可以在VS的集成平台上输出调试信息&#xff0c;也可以使用DebugView来查看调试信息…

【强连通分量+概率】Bzoj2438 杀人游戏

Description 一位冷血的杀手潜入 Na-wiat&#xff0c;并假装成平民。警察希望能在 N 个人里面&#xff0c;查出谁是杀手。 警察能够对每一个人进行查证&#xff0c;假如查证的对象是平民&#xff0c;他会告诉警察&#xff0c;他认识的人&#xff0c; 谁是杀手&#xff0c; 谁是…

serialversionuid的作用_为什么阿里Java规约要求谨慎修改serialVersionUID字段

serialVersionUID简要介绍serialVersionUID是在Java序列化、反序列化对象时起作用的一个字段。Java的序列化机制是通过判断类的serialVersionUID来验证版本一致性的。在进行反序列化时&#xff0c;JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进…

常用工具说明--搭建基于rietveld的CodeReview平台(未测试)

为什么要codereview . 整个团队的编码风格是统一的。 . 有高手能对自己的代码指点一二&#xff0c;从而提高编码水平。 . 减少低级错误的出现 . 约束自己写高质量的代码&#xff0c;因为是要给人看的。 我们对codereview的需求 . 很轻松可以发布自己写的代码。 . 很轻松的可以与…

postman调用webservice接口_接口对前后端和测试的意义

1.什么是接口&#xff1f;接口测试主要用于外部系统与系统之间以及内部各个子系统之间的交互点&#xff0c;定义特定的交互点&#xff0c;然后通过这些交互点来&#xff0c;通过一些特殊的规则也就是协议&#xff0c;来进行数据之间的交互。2.接口都有哪些类型&#xff1f;接口…

基于代数距离的椭圆拟合

问题 给定离散点集Xi(xi,yi),i1,2,...NX_i(x_i,y_i) ,i1,2,...NXi​(xi​,yi​),i1,2,...N&#xff0c;我们希望找到误差最小的椭圆去拟合这些离散点。 方法 由于椭圆的形式可以给定&#xff0c; 自然我们将使用最小二乘法来求解椭圆。主要依据论文《Direct least squares f…

Android调试技巧之Eclipse行号和Logcat

很多初入Android的开发者可能会发现经常遇到Force Close或ANR这样的问题&#xff0c;一般我们通过Android系统的错误日志打印工具Logcat可以看到出错的内容&#xff0c;今天一起来说下如何通过 Eclipse行号和Logcat捕捉出错点&#xff0c;我们遇到错误可以首先在Eclipse的DDMS中…

js打开本地文件夹_vue + ArcGIS 地图应用系列一:arcgis api本地部署(开发环境)

1. 下载 ArcGIS API for JavaScript 官网地址&#xff1a; https://developers.arcgis.com/javascript/3/ 下载地址&#xff1a;http://links.esri.com/javascript-api/latest-download需要稳定的网络环境注册账号后才可以下载下载完成后解压文件&#xff0c;文件比较大可能需要…

基于几何距离的椭圆拟合

问题 给定离散点集Xi(xi,yi)X_i(x_i,y_i)Xi​(xi​,yi​)&#xff0c;我们希望找到最好的椭圆去拟合这些离散点。 方法 通常我们使用最小二乘法求解如下的最优化问题&#xff1a; Min∑i1Nf(xi,E)2Min \sum_{i1}^N f(x_i,E)^2 Mini1∑N​f(xi​,E)2 这里f(xi,E)f(x_i,E)f(xi…

nohup命令输出日志_逼格高又实用的Linux高级命令,开发运维都要懂

在运维的坑里摸爬滚打好几年了&#xff0c;我还记得我刚开始的时候&#xff0c;我只会使用一些简单的命令&#xff0c;写脚本的时候&#xff0c;也是要多简单有多简单&#xff0c;所以有时候写出来的脚本又长又臭&#xff0c;像一些高级点的命令&#xff0c;比如说Xargs 命令、…

Android SimpleAdapter的参数

1.作用是ArrayList和 ListView的桥梁。这个ArrayList里边的每一项都是一个Map<String,?>类型。 ArrayList当中的每一项 Map对象都和ListView里边的每一项进行数据绑定一一对应。2.SimpleAdapter的构造函数&#xff1a;SimpleAdapter(Context context, List<? …

一个excel文档里复制黏贴另外表单跟着变动_利用Excel连接Power BI,实现PPT报告自动输出...

​文/HALI就职于汽车行业战略部门 专注汽车市场信息情报收集和分析因为工作需要&#xff0c;每月周期性的更新数据和撰写PPT 报告成为繁重的劳动。结果是很多时间花费在数据处理上&#xff0c;真正的分析工作&#xff0c;往往只能草草收场。不能坐以待毙&#xff0c;就要想想有…

出现23.97帧率的原因

http://raytao.lofter.com/post/3d177_185a386 关于那些“格&#xff08;Frame&#xff09;”不得不说的事 今天早上&#xff0c;鄙人在社交网络发了这一系列的问题&#xff1a;请解释以下名词之间的关系或差异。帧&#xff0c;格&#xff0c;帧率&#xff0c;时基&#xff0c;…

使用ajax将数据显示在指定位置_AJAX学习主题之一

学习主题&#xff1a;AJAX删除用户功能实现根据视频中的讲解&#xff0c;完成以下内容简述删除功能的基本思路流程点击按钮获取当前元素中的用户uid&#xff0c;向服务器发起请求&#xff0c;将uid提交到服务器删除指定用户&#xff0c;浏览器获取浏览器响应结果。独立完成删除…

js日期比较大小_node.js 内存泄漏的秘密

每日前端夜话第276篇翻译&#xff1a;疯狂的技术宅作者&#xff1a;Giovanny Gongora来源&#xff1a;nodesource正文共&#xff1a;3955 字预计阅读时间&#xff1a;10分钟一直以来&#xff0c;跟踪 Node.js 的内存泄漏是一个反复出现的话题&#xff0c;人们始终希望对其复杂性…

win7+vs2015/13+caffe+matlab+python(CPU only)配置

首先声明本教程可以适用于vs2015 和vs2013 .以vs2015为例。 安装必备软件 vs 2015 /vs2013 matlab 2016a(64bit)推荐使用Anaconda 2.7 或者Miniconda 2.7这两个Python发布版本cmake 3.8.0 以上caffe-window: https://github.com/BVLC/caffe/tree/windows 可选软件&#xff1…