【C++专题】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是两个不同的对象。

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

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

相关文章

浅谈程序员的职业规划

不知不觉参加工作成为程序员已经4年多&#xff0c;记得上高中的时候&#xff0c;从网络上知道了很多IT精英创业成功的例子&#xff0c;如没有读过大学的“汽车之家”创始人李想、facebook创始人马克扎克伯格&#xff0c;让我觉得互联网是个充满梦想的舞台&#xff0c;只要有想法…

python调用数据库数据创建函数_Pyhton应用程序数据库函数封装

1.函数2.迭代器3.递归4.数据库函数5.fetchall函数1.函数&#xff1a;实现指定功能代码的集合def 函数名( ) :2.在python中没有括号&#xff0c;函数体以缩进的方式进行展示快捷键&#xff1a;tab实现了函数的缩进&#xff0c;shifttab实现前移3.调用&#xff1a;函数名( )作用&…

电子计算机和多媒体教材分析,人教新课标:电子计算机与多媒体教材分析

电子计算机与多媒体(4篇)主要内容&#xff1a;本文简要地介绍了电子计算机的发明到多媒体的运用的基本情况&#xff0c;展示了电子计算机的飞速发展和灿烂前景。课文从美国史密森博物馆里存放的世界上第一台电子计算机写起&#xff0c;先概括地交代了电子计算机的飞速发展和它在…

安卓工控主板运行时会自动重启_工控主板在工业自动化中的应用

原标题&#xff1a;工控主板在工业自动化中的应用大家都知道随着科技的发展对于工控主板的用途和应用大大超出了工业自动化的范围&#xff0c;而对于本文联智通达小编将仅坚持工业自动化范围内的应用。首先跟随联智通达小编看一下制造以及工业PC的应用以及如何使该领域的工业自…

am335x gpio驱动

任务: GPIO0_19(带下拉)作为中断, GPIO0_20(带上拉)和GPIO1_14作为输出管脚&#xff0c;GPIO0_11(带下拉)和 GPIO1_15(带上拉)。并编写驱动程序。

海量小文件存储

海量小文件存储 [转自:http://www.fuchaoqun.com/2009/04/deal-with-tons-of-small-files/] Web2.0网站&#xff0c;数据内容以几何级数增长&#xff0c;尤其是那些小文件&#xff0c;几K&#xff5e;几百K不等&#xff0c;数量巨多&#xff0c;传统的文件系统处理起来很是吃力…

与0xf2值相等的是python_腾讯笔试题涵盖的基础知识

1.下列减少内存碎片的方法有哪些是正确的&#xff1f;增加实际申请和释放的次数频繁调用的子函数尽量使用栈内存系统申请一大块内存&#xff0c;自己实现内存分配和释放&#xff0c;定时清理内存降低虚拟内存的大小解答&#xff1a;答案2,3是正确的。属于操作系统中内存管理的问…

重庆大学 计算机组成原理,重庆大学计算机组成原理集(含部分)解决方案.doc

《计算机组成原理》试题集一、选择题在每小题列出的四个备选项中只有一个是符合题目要求的&#xff0c;请将其代码填写在题后的括号内。1&#xff0e;反映计算机基本功能的是( )A)操作系统 B)系统软件 C)指令系统 D)数据库系统2&#xff0e;若二进制数为1111.101&#xff0c;则…

diff算法_vue源码解读 diff算法

导语 最近碰到部分业务场景&#xff0c;代码逻辑需要了解"数组变更后&#xff0c;具体变更了哪一些元素&#xff0c;以及变更的位置.."。于是仔细研究并覆写了一遍针对数组变化的diff算法&#xff0c;在这里做下diff算法的逻辑分享&&源码解读一.介绍前的准备…

Linux驱动模块编译进内核中

BQ27501驱动编译进内核 一、 驱动程序编译进内核的步骤 在 linux 内核中增加程序需要完成以下三项工作&#xff1a; 1. 将编写的源代码复制到 Linux 内核源代码的相应目录&#xff1b; 2. 在目录的 Kconfig 文件中增加新源代码对应项目的编译配置选项&#xff1b; 3. 在…

oracle 的进程

W000: Wnnn命名为W000&#xff0c;W001&#xff0c;W002.....&#xff0c;由smcO动态产生执行上述相关任务。 Pmon: Pmon后台进程负责一下的工作:进程异常终止,会话被杀掉,事务超过空闲时间,网络连接超时,将实例信息注册到监听器上,手工注册 altersystem register; Pmon进程的清…

请简述计算机硬件系统的运行过程,操作系统简述题

✔什么是操作系统&#xff1f;它的功能&#xff1f;操作系统是控制和管理计算机硬件和软件资源&#xff0c;合理地组织计算机工作流程以及方便用户使用计算机系统的程序的集合。功能&#xff1a;处理机管理&#xff0c;存储器管理&#xff0c;I/O设备管理和文件管理以及作为操作…

python闭环最短路径_最短路径算法的实现(dijskstra):Python

dijskstra最短路径算法步骤&#xff1a;输入&#xff1a;图G(V(G),E(G))有一个源顶点S和一个汇顶点t&#xff0c;以及对所有的边ij属于E(G)的非负边长出cij。输出&#xff1a;G从s到t的最短路径的长度。第0步&#xff1a;从对每个顶点做临时标记L开始&#xff0c;做法如下&…

黑群晖二合一安装不了套件_玩PT还是得安装transmission,星际蜗牛安装黑群晖制作家用NAS...

原文作者&#xff1a;pt老萌新To小白&#xff1a;黑群晖docker安装PT神器transmission——星际蜗牛安装黑群晖制作家用NAS的折腾日记写在前面&#xff1a;里面的技术方法不是我原创的&#xff0c;都是网上找的&#xff0c;侵删。折腾的过程记录是原创的(好像没啥原创的了)&…

Know more about Cache Buffer Handle

在之前的文章《latch free:cache buffer handles造成的SQL性能问题》中我介绍了cache buffer handle latch的一些知识&#xff0c;在这里我们复习一下&#xff1a; "当会话需要pin住buffer header时它首先要获去buffer handle&#xff0c;得到buffer handle的过程中首先要…

spring boot web项目_阿里技术专家带你使用Spring框架快速搭建Web工程项目

点击上方 "程序员小乐"关注, 星标或置顶一起成长 第一时间与你相约 每日英文 We all have a past. It’s how you deal with it. 每个人都有过去&#xff0c;只是取决于你怎么去处理。 每日掏心话 人不都是这样吗&#xff0c;安慰别人的时候头头是道&#xff0c;自己…

MySQL执行外部sql脚本文件的命令

sql脚本是包含一到多个sql命令的sql语句&#xff0c;我们可以将这些sql脚本放在一个文本文件中&#xff08;我们称之为“sql脚本文件”&#xff09;&#xff0c;然后通过相关的命令执行这个sql脚本文件。基本步骤如下&#xff1a;1、创建包含sql命令的sql脚本文件 文件中包含一…

全国计算机水平考试技巧,全国计算机等级考试上机考试应试技巧

掌握好上机考试的应试技巧&#xff0c;可以使考生的实际水平在考试时得到充分发挥&#xff0c;从而取得较为理想的成绩。历次考试均有考生因为忽略了这一点&#xff0c;加之较为紧张的考场气氛影响了水平的发挥&#xff0c;致使考试成绩大大低于实际水平。因此每个考生在应试前…

git 代码回滚_能提交到远程的Git回滚

很多情况下我们需要回滚代码&#xff0c;最容易想到的就是git reset。但是git reset有个弱点&#xff0c;它是一个彻底的回滚&#xff0c;不能再提交给远程了&#xff0c;因为在提交记录里回滚点之后的记录都不见了。做一下试验&#xff0c;一个文件我们提交了三次之后回滚#往前…

Mysql数据库的几大优势

为什么开源社区买mysql的帐&#xff0c; MySQL数据库的吸引开源社区的几大特质: 客户为什么会考虑购买MySQL 企业版Enterprise Edition : 转载于:https://www.cnblogs.com/macleanoracle/archive/2013/03/19/2968208.html