static_cast、dynamic_cast、const_cast和reinterpret_cast总结

2023-10-30

文章转载自:http://www.jellythink.com/archives/205

前言

这篇文章总结的是C++中的类型转换,这些小的知识点,有的时候,自己不是很注意,但是在实际开发中确实经常使用的。俗话说的好,不懂自己写的代码的程序员,不是好的程序员;如果一个程序员对于自己写的代码都不懂,只是知道一昧的的去使用,终有一天,你会迷失你自己的。

C++中的类型转换分为两种:

  1. 隐式类型转换;
  2. 显式类型转换。

而对于隐式变换,就是标准的转换,在很多时候,不经意间就发生了,比如int类型和float类型相加时,int类型就会被隐式的转换位float类型,然后再进行相加运算。而关于隐式转换不是今天总结的重点,重点是显式转换。在标准C++中有四个类型转换符:static_castdynamic_castconst_castreinterpret_cast;下面将对它们一一的进行总结。

 

static_cast

static_cast的转换格式:static_cast <type-id> (expression)

将expression转换为type-id类型,主要用于非多态类型之间的转换,不提供运行时的检查来确保转换的安全性。主要在以下几种场合中使用:

  1. 用于类层次结构中,基类和子类之间指针和引用的转换;
    当进行上行转换,也就是把子类的指针或引用转换成父类表示,这种转换是安全的;
    当进行下行转换,也就是把父类的指针或引用转换成子类表示,这种转换是不安全的,也需要程序员来保证;
  2. 用于基本数据类型之间的转换,如把int转换成char,把int转换成enum等等,这种转换的安全性需要程序员来保证;
  3. 把void指针转换成目标类型的指针,是及其不安全的;

注:static_cast不能转换掉expression的const、volatile和__unaligned属性。

 

dynamic_cast

dynamic_cast的转换格式:dynamic_cast <type-id> (expression)

将expression转换为type-id类型,type-id必须是类的指针、类的引用或者是void *;如果type-id是指针类型,那么expression也必须是一个指针;如果type-id是一个引用,那么expression也必须是一个引用。

dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。在多态类型之间的转换主要使用dynamic_cast,因为类型提供了运行时信息。下面我将分别在以下的几种场合下进行dynamic_cast的使用总结:

    1.最简单的上行转换

比如B继承自A,B转换为A,进行上行转换时,是安全的,如下:

#include <iostream>
using namespace std;
class A
{
     // ......
};
class B : public A
{
     // ......
};
int main()
{
     B *pB = new B;
     A *pA = dynamic_cast<A *>(pB); // Safe and will succeed
}

 

    2.多重继承之间的上行转换

C继承自B,B继承自A,这种多重继承的关系;但是,关系很明确,使用dynamic_cast进行转换时,也是很简单的:

class A
{
     // ......
};
class B : public A
{
     // ......
};
class C : public B
{
     // ......
};
int main()
{
     C *pC = new C;
     B *pB = dynamic_cast<B *>(pC); // OK
     A *pA = dynamic_cast<A *>(pC); // OK
}

      3.而上述的转换,static_cast和dynamic_cast具有同样的效果。

而这种上行转换,也被称为隐式转换;比如我们在定义变量时经常这么写:B *pB = new C;这和上面是一个道理的,只是多加了一个dynamic_cast转换符而已。

      3.转换成void *

可以将类转换成void *,例如:

class A
{
public:
     virtual void f(){}
     // ......
};
class B
{
public:
     virtual void f(){}
     // ......
};
int main()
{
     A *pA = new A;
     B *pB = new B;
     void *pV = dynamic_cast<void *>(pA); // pV points to an object of A
     pV = dynamic_cast<void *>(pB); // pV points to an object of B
}

但是,在类A和类B中必须包含虚函数,为什么呢?因为类中存在虚函数,就说明它有想让基类指针或引用指向派生类对象的情况,此时转换才有意义;由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表中,只有定义了虚函数的类才有虚函数表。

    4.如果expression是type-id的基类,使用dynamic_cast进行转换时,在运行时就会检查expression是否真正的指向一个type-id类型的对象,如果是,则能进行正确的转换,获得对应的值;否则返回NULL,如果是引用,则在运行时就会抛出异常;例如:

class B
{
     virtual void f(){};
};
class D : public B
{
     virtual void f(){};
};
void main()
{
     B* pb = new D;   // unclear but ok
     B* pb2 = new B;
     D* pd = dynamic_cast<D*>(pb);   // ok: pb actually points to a D
     D* pd2 = dynamic_cast<D*>(pb2);   // pb2 points to a B not a D, now pd2 is NULL
}

这个就是下行转换,从基类指针转换到派生类指针。

对于一些复杂的继承关系来说,比如:多重继承的情况,从派生类往父类的父类进行转时,需要特别注意。

有如下的一个结构:

B继承A、C继承A、D又继承B和C。

D类型可以安全的转换成B和C类型,但是D类型要是直接转换成A类型呢?

class A
{
     virtual void Func() = 0;
};
class B : public A
{
     void Func(){};
};
class C : public A
{
     void Func(){};
};
class D : public B, public C
{
     void Func(){}
};
int main()
{
     D *pD = new D;
     A *pA = dynamic_cast<A *>(pD); // You will get a pA which is NULL
}

如果进行上面的直接转,你将会得到一个NULL的pA指针;这是因为,B和C都继承了A,并且都实现了虚函数Func,导致在进行转换时,无法进行抉择应该向哪个A进行转换。正确的做法是:

int main()
{
     D *pD = new D;
     B *pB = dynamic_cast<B *>(pD);
     A *pA = dynamic_cast<A *>(pB);
}

上面就是对于dynamic_cast转换的一些细节知识点,特别是对于多重继承的情况,在实际项目中,很容易出现问题。

 

const_cast

const_cast的转换格式:const_cast <type-id> (expression)

const_cast用来将类型的const、volatile和__unaligned属性移除。常量指针被转换成非常量指针,并且仍然指向原来的对象;常量引用被转换成非常量引用,并且仍然引用原来的对象。看以下的代码例子:

/*
** FileName     : ConstCastDemo
** Author       : Jelly Young
** Date         : 2013/12/27
** Description  : More information, please go to http://www.jellythink.com
*/
#include <iostream>
using namespace std;
class CA
{
public:
     CA():m_iA(10){}
     int m_iA;
};
int main()
{
     const CA *pA = new CA;
     // pA->m_iA = 100; // Error
     CA *pB = const_cast<CA *>(pA);
     pB->m_iA = 100;
     // Now the pA and the pB points to the same object
     cout<<pA->m_iA<<endl;
     cout<<pB->m_iA<<endl;
     const CA &a = *pA;
     // a.m_iA = 200; // Error
     CA &b = const_cast<CA &>(a);
     b.m_iA = 200;
     // Now the a and the b reference to the same object
     cout<<b.m_iA<<endl;
     cout<<a.m_iA<<endl;
}

注:你不能直接对非指针和非引用的变量使用const_cast操作符去直接移除它的const、volatile和__unaligned属性。

 

reinterpret_cast

reinterpret_cast的转换格式:reinterpret_cast <type-id> (expression)

允许将任何指针类型转换为其它的指针类型;听起来很强大,但是也很不靠谱。它主要用于将一种数据类型从一种类型转换为另一种类型。它可以将一个指针转换成一个整数,也可以将一个整数转换成一个指针,在实际开发中,先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原来的指针值;特别是开辟了系统全局的内存空间,需要在多个应用程序之间使用时,需要彼此共享,传递这个内存空间的指针时,就可以将指针转换成整数值,得到以后,再将整数值转换成指针,进行对应的操作。

 

总结

这篇博文总结了C++中的类型转换,重点总结了其中的显式转换。对于C++支持的这四种显式转换都进行了详细的描述。如果大家有什么补充的,或者我总结的有误的地方,请大家多多指教。

听闻雷哥在北京面试不顺,公司对数据结构和算法都要求很多,过段日子准备再将数据结构和算法进行整理一下。

2013年12月27日 于大连,东软。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

static_cast、dynamic_cast、const_cast和reinterpret_cast总结 的相关文章

随机推荐

  • 使用亚马逊云科技人工智能内容审核服务,打造安全的图像生成和扩散模型

    生成式人工智能技术发展日新月异 现在已经能够根据文本输入生成文本和图像 Stable Diffusion 是一种文本转图像模型 可让您创建栩栩如生的图像应用 您可以通过 Amazon SageMaker JumpStart 使用 Stabl
  • vue3介绍,Vue3的新特性

    vue3的优势 Vue 是目前国内开发最火的前端框架之一 react vue angular的下载趋势 Vue3性能更高 体积更小 Vue的compositionAPI 组合式API 可以 更好的代码复用 方便构建大型项目 对TS的支持比较
  • 通过DButils与连接池C3P0实现对对数据库的增删改查操作

    需要准备的有 C3P0配置文件
  • Excel匹配两列相同内容到同一行

    1 打开一个excel文件 找到需要自动对齐的两列数据 这里根据需要模拟了两列 2 用鼠标左键单击选中C1单元格 3 在C1单元格内输入公式 IF COUNTIF B 1 B 156 A1 0 A1 4 输入完公式后回车 再次选中C1单元格
  • k8s面试题大全(持续更新中)

    目录 前言 docker的工作原理是什么 讲一下 docker的组成包含哪几大部分 docker与传统虚拟机的区别什么 docker技术的三大核心概念是什么 centos镜像几个G 但是docker centos镜像才几百兆 这是为什么 讲
  • Bootstrap验证

    1 readonly验证需要 重新更新 var bootstrapValidator notice info input form data bootstrapValidator bootstrapValidator updateStatu
  • MySql安装版安装最新教程(附错误解决 )

    目录 1 安装版自定义安装 2 问题总结 1 安装版自定义安装 如果需要安装版安装 可参考另一篇博客 MySql压缩版安装最新教程 附错误解决 链接 MySql官网 滑到最下面 上面的是企业版下载 需要付费 我们下载开源的社区版 在这里我下
  • 编译出现 WARNING: ‘aclocal-1.15‘ is missing on your system.问题解决

    最近在中标麒麟下编译opus 1 2 1 make时出现了WARNING aclocal 1 15 is missing on your system 问题 发现是版本问题 于是重新安装了automake 下面是安装过程 1 下载安装包 h
  • PID恒温控制

    1 PID三个环节的作用 PID三个环节各自的主要作用和效应 比例环节 起主要控制作用 使控制量向目标值靠拢 但可能导致振荡 积分环节 消除稳态误差 但会增加超调量 微分环节 产生阻尼效果 抑制振荡和超调 但会降低响应速度 2 比例系数与积
  • @NotEmpty、@NotBlank、@NotNull 区别和使用

    首先需要说明下 本提到的 NotEmpty NotBlank NotNull 分别是 javax validation constraints NotEmpty javax validation constraints NotBlank j
  • Menu菜单,MenuBar菜单栏,MenuItem菜单项

    Menu菜单 MenuBar菜单栏 MenuItem菜单项 菜单栏 public class MenuDemo public static void main String args Frame f new Frame Menu菜单 Men
  • WEB攻防-通用漏洞&水平垂直越权&购买逻辑漏洞

    目录 水平垂直越权 水平越权 垂直越权 访问控制原理 漏洞判别 防护 购买逻辑漏洞 知识点 详细介绍 防护 案例演示 优惠券 案例演示 CMS 订单修改 水平垂直越权 水平越权 同级用户权限共享 当用户访问数据时未对用户和id值进行有效的查
  • Qt-QPointer的使用

    在使用Qt的时候 你是否遇到过这样的场景 从外部传来一个QObject的指针 当使用这个指针的时候 害怕它已经被释放了 如果我们在一个对象A中引用了另一个对象B 当对象B被析构的时候 A对象其实是不知道B已经被析构 这个时候再使用B的话就会
  • 【技术分享】如何实现功能完备性能优异的RTMP、RTSP播放器?

    技术背景 这几年 我们对接了太多有RTSP或RTMP直播播放器诉求的开发者 他们当中除了寻求完整的解决方案的 还有些是技术探讨 希望能借鉴我们播放端的开发思路或功能特性 完善自己的产品 忙里偷闲 今天我们就再聊一聊老生常谈的问题 如何实现功
  • IntelliJ IDEA 2023.2.1 Android开发变化

    IntelliJ IDEA 2023 2 1之前的版本 Empty Activity是指Empty View Activity 而现在Empty Activity是指Empty Compose Activity 另外多了一个Empty Vi
  • 使用this调用vue中定义的,data中数据或methods中的方法,报错undefined的原因及解决办法

    问题描述 使用this调用vue中定义的data时 报错 这个变量undefined
  • Jenkins+RobotFramework 失败用例重执行方案!

    背景 接口测试用例运行在Jenkins节点上 在某些情况下 比如网络波动等原因 会导致用例运行失败 此时会触发邮件和钉钉预警 通知给到责任人 按照现有策略 当本次构建失败时 会立马触发第二次构建活动 若第二次构建仍然失败 则会再次触发预警信
  • Redis 面试一定要知道的 3 个 问题!

    一 缓存穿透 缓存穿透是指当用户在查询一条数据的时候 而此时数据库和缓存却没有关于这条数据的任何记录 而这条数据在缓存中没找到就会向数据库请求获取数据 它拿不到数据时 是会一直查询数据库 这样会对数据库的访问造成很大的压力 如 用户查询一个
  • Linux(WSL)安装CUDA

    选择CUDA版本 nvidia smi 选择的CUDA版本要比 CUDA version 的版本更低 ps 本机之前已经把CUDA驱动器在windows端安装完成 安装 CUDA 参考 Windows10 11 WSL2 安装nvidia
  • static_cast、dynamic_cast、const_cast和reinterpret_cast总结

    文章转载自 http www jellythink com archives 205 前言 这篇文章总结的是C 中的类型转换 这些小的知识点 有的时候 自己不是很注意 但是在实际开发中确实经常使用的 俗话说的好 不懂自己写的代码的程序员 不