快速理解关于括号运算符、static_cast、dynamic_cast和reinterpret_cast

2024-02-24 02:08

本文主要是介绍快速理解关于括号运算符、static_cast、dynamic_cast和reinterpret_cast,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

N年以前,年轻无知的偶写了一篇很2的贴子:

http://blog.csdn.net/superarhow/article/details/1007875

不知道有没有误导读者。好在从阅读数量来看应该不会误导很多人吧。。。

关于这几个运算符的区别,各个地方的资料已经很多了。这篇文章是希望用比较浅显易懂的表达方式,写给希望快速理解它们,以及了解不正确使用它们会带来什么后果的读者们看的。笔者水平有限,如有疏漏之处,还请不吝指正。

首先指出,括号运算符是可以完成所有的转换的。那么第一个问题就是:为什么C++要引入这么几个cast?既然括号就已经足够了?

来看下面这一个例子:

class CItem { public: void SetOwner(void* o) { _owner = o; } public: void* _owner; };

class CBanana { };

template<typename ItemClass_> class CContainer { public: void AddItem(int index, ItemClass_* i) { _items[index] = (CItem*)i; ((CItem*)i)->SetOwner(this); } public: CItem*  _items[10]; };

int main(int argc, char** argv) { CItem* item = new CItem; CContainer<CItem> c; c.AddItem(0, item); CContainer<CBanana> basket; CBanana b; basket.AddItem(0, &b);   // <---- Here return 0; }

CContainer希望它的子类继承自CItem,以调用它的SetOwner方法。因此在AddItem而是在中间用了(CItem*)这样的cast。
第一个对AddItem的调用是没有问题的。第二个调用,可以看到,CBanana类既不是CItem类的子类,而且也没有实现SetOwner方法,但是编译器没有给出任何警告!运行这段程序将毫无疑问的引起程序crash。
所有的cast运算符,都是为了实现“将X当作Y"的功能。那么就会有下面两个问题:

1. 如何将X当作Y?

2. X能不能被当作Y?如果不能,怎么办?

例如:如何把一个整数当作一个指针?它们本是无关的东西。隐含的操作是这个整数是指针的地址。这种转换是reinterpret_cast。即不管原来类型如何,都以它们各自自己的意义解释,进行转化。在我们上面这个例子中,括号就相当于完成了一次reinterpret_cast。

关于问题2,我们将上面的例子改为用static_cast,那么在编译时,编译器将会出错:

 error: invalid static_cast from type 'CBanana*' to type 'CItem*'

这就达到了我们的要求。

dynamic_cast和static_cast类似,更为强大的是,它会在运行时检查指针的类型,如果类型不一致,则会返回NULL。当然它需要程序编译时有RTTI信息。

所以简而言之,static_cast和dynamic_cast都是用于对象类型的转换。

reinterpret_cast还有什么危险性呢?再看一个例子:

class CFruit
{
public:virtual void drink() {printf("%s\n", _juice);}CFruit() : _juice("tomato ice") {}
private:char*  _juice;
};class CVegetable
{
public:virtual void cook() {printf("%s\n", _dish);}CVegetable() : _dish("tomato egg") {}
private:char*  _dish;
};class CTomato : public CFruit, public CVegetable
{
public:virtual void eat() {drink();cook();}
};

 

int main(int argc, char** argv) { CTomato* tomato = new CTomato; /// 1 ((CVegetable*)tomato)->cook(); ((CFruit*)tomato)->drink();

    /// 2 void* p = tomato; ((CVegetable*)p)->cook(); ((CFruit*)p)->drink();

    /// 3 p = (CVegetable*)tomato; ((CVegetable*)p)->cook(); ((CFruit*)p)->drink();

    printf("(CTomato*)tomato=%p (CFruit*)tomato=%p (CVegetable*)tomato=%p\n", (CTomato*)tomato,   (CFruit*)tomato,   (CVegetable*)tomato); return 0; } 

猜猜看第1,2,3段都会打印出什么?然后再让最后一句printf来告诉你答案。

第1段会打印出正确结果,而2则两个都是tomato ice;3则两个都是tomato egg。

我们这里的括号也是reinterpret_cast,因为void*不含任何类型信息。而在一个多重继承的对象中,CTomato的内存组织如下:

+0 [CTomato/CFruit]  +N [CVegetable]

CTomato和CFruit的部份是指向同一个地址,而CVegetable则需要一个偏移。因此,在使用reinterpret_cast时,CTomato和CFruit不会有任何问题,但CVegetable的部份就会出错了。以上例子只是出错,而实际使用中,因为对象布局的不同,通常都是以crash告终。
那么为什么1的部份正确呢?因为它是对象间的转换,有对象信息,因此编译器实际上是使用了static_cast来进行的转换。这个转换翻译成汇编语言,也就是加或减去CVegetable和CTomato之间的对象偏移值。

这篇关于快速理解关于括号运算符、static_cast、dynamic_cast和reinterpret_cast的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/740620

相关文章

深入理解Go语言中二维切片的使用

《深入理解Go语言中二维切片的使用》本文深入讲解了Go语言中二维切片的概念与应用,用于表示矩阵、表格等二维数据结构,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧... 目录引言二维切片的基本概念定义创建二维切片二维切片的操作访问元素修改元素遍历二维切片二维切片的动态调整追加行动态

MySQL 中的 CAST 函数详解及常见用法

《MySQL中的CAST函数详解及常见用法》CAST函数是MySQL中用于数据类型转换的重要函数,它允许你将一个值从一种数据类型转换为另一种数据类型,本文给大家介绍MySQL中的CAST... 目录mysql 中的 CAST 函数详解一、基本语法二、支持的数据类型三、常见用法示例1. 字符串转数字2. 数字

从原理到实战深入理解Java 断言assert

《从原理到实战深入理解Java断言assert》本文深入解析Java断言机制,涵盖语法、工作原理、启用方式及与异常的区别,推荐用于开发阶段的条件检查与状态验证,并强调生产环境应使用参数验证工具类替代... 目录深入理解 Java 断言(assert):从原理到实战引言:为什么需要断言?一、断言基础1.1 语

C++20管道运算符的实现示例

《C++20管道运算符的实现示例》本文简要介绍C++20管道运算符的使用与实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录标准库的管道运算符使用自己实现类似的管道运算符我们不打算介绍太多,因为它实际属于c++20最为重要的

解析C++11 static_assert及与Boost库的关联从入门到精通

《解析C++11static_assert及与Boost库的关联从入门到精通》static_assert是C++中强大的编译时验证工具,它能够在编译阶段拦截不符合预期的类型或值,增强代码的健壮性,通... 目录一、背景知识:传统断言方法的局限性1.1 assert宏1.2 #error指令1.3 第三方解决

Linux如何快速检查服务器的硬件配置和性能指标

《Linux如何快速检查服务器的硬件配置和性能指标》在运维和开发工作中,我们经常需要快速检查Linux服务器的硬件配置和性能指标,本文将以CentOS为例,介绍如何通过命令行快速获取这些关键信息,... 目录引言一、查询CPU核心数编程(几C?)1. 使用 nproc(最简单)2. 使用 lscpu(详细信

CSS中的Static、Relative、Absolute、Fixed、Sticky的应用与详细对比

《CSS中的Static、Relative、Absolute、Fixed、Sticky的应用与详细对比》CSS中的position属性用于控制元素的定位方式,不同的定位方式会影响元素在页面中的布... css 中的 position 属性用于控制元素的定位方式,不同的定位方式会影响元素在页面中的布局和层叠关

一文详解如何在idea中快速搭建一个Spring Boot项目

《一文详解如何在idea中快速搭建一个SpringBoot项目》IntelliJIDEA作为Java开发者的‌首选IDE‌,深度集成SpringBoot支持,可一键生成项目骨架、智能配置依赖,这篇文... 目录前言1、创建项目名称2、勾选需要的依赖3、在setting中检查maven4、编写数据源5、开启热

MybatisX快速生成增删改查的方法示例

《MybatisX快速生成增删改查的方法示例》MybatisX是基于IDEA的MyBatis/MyBatis-Plus开发插件,本文主要介绍了MybatisX快速生成增删改查的方法示例,文中通过示例代... 目录1 安装2 基本功能2.1 XML跳转2.2 代码生成2.2.1 生成.xml中的sql语句头2

8种快速易用的Python Matplotlib数据可视化方法汇总(附源码)

《8种快速易用的PythonMatplotlib数据可视化方法汇总(附源码)》你是否曾经面对一堆复杂的数据,却不知道如何让它们变得直观易懂?别慌,Python的Matplotlib库是你数据可视化的... 目录引言1. 折线图(Line Plot)——趋势分析2. 柱状图(Bar Chart)——对比分析3