快速理解关于括号运算符、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

相关文章

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

电脑桌面文件删除了怎么找回来?别急,快速恢复攻略在此

在日常使用电脑的过程中,我们经常会遇到这样的情况:一不小心,桌面上的某个重要文件被删除了。这时,大多数人可能会感到惊慌失措,不知所措。 其实,不必过于担心,因为有很多方法可以帮助我们找回被删除的桌面文件。下面,就让我们一起来了解一下这些恢复桌面文件的方法吧。 一、使用撤销操作 如果我们刚刚删除了桌面上的文件,并且还没有进行其他操作,那么可以尝试使用撤销操作来恢复文件。在键盘上同时按下“C

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝

hdu 4565 推倒公式+矩阵快速幂

题意 求下式的值: Sn=⌈ (a+b√)n⌉%m S_n = \lceil\ (a + \sqrt{b}) ^ n \rceil\% m 其中: 0<a,m<215 0< a, m < 2^{15} 0<b,n<231 0 < b, n < 2^{31} (a−1)2<b<a2 (a-1)^2< b < a^2 解析 令: An=(a+b√)n A_n = (a +

native和static native区别

本文基于Hello JNI  如有疑惑,请看之前几篇文章。 native 与 static native java中 public native String helloJni();public native static String helloJniStatic();1212 JNI中 JNIEXPORT jstring JNICALL Java_com_test_g

v0.dev快速开发

探索v0.dev:次世代开发者之利器 今之技艺日新月异,开发者之工具亦随之进步不辍。v0.dev者,新兴之开发者利器也,迅速引起众多开发者之瞩目。本文将引汝探究v0.dev之基本功能与优势,助汝速速上手,提升开发之效率。 何谓v0.dev? v0.dev者,现代化之开发者工具也,旨在简化并加速软件开发之过程。其集多种功能于一体,助开发者高效编写、测试及部署代码。无论汝为前端开发者、后端开发者

深入理解RxJava:响应式编程的现代方式

在当今的软件开发世界中,异步编程和事件驱动的架构变得越来越重要。RxJava,作为响应式编程(Reactive Programming)的一个流行库,为Java和Android开发者提供了一种强大的方式来处理异步任务和事件流。本文将深入探讨RxJava的核心概念、优势以及如何在实际项目中应用它。 文章目录 💯 什么是RxJava?💯 响应式编程的优势💯 RxJava的核心概念

如何通俗理解注意力机制?

1、注意力机制(Attention Mechanism)是机器学习和深度学习中一种模拟人类注意力的方法,用于提高模型在处理大量信息时的效率和效果。通俗地理解,它就像是在一堆信息中找到最重要的部分,把注意力集中在这些关键点上,从而更好地完成任务。以下是几个简单的比喻来帮助理解注意力机制: 2、寻找重点:想象一下,你在阅读一篇文章的时候,有些段落特别重要,你会特别注意这些段落,反复阅读,而对其他部分

深入理解数据库的 4NF:多值依赖与消除数据异常

在数据库设计中, "范式" 是一个常常被提到的重要概念。许多初学者在学习数据库设计时,经常听到第一范式(1NF)、第二范式(2NF)、第三范式(3NF)以及 BCNF(Boyce-Codd范式)。这些范式都旨在通过消除数据冗余和异常来优化数据库结构。然而,当我们谈到 4NF(第四范式)时,事情变得更加复杂。本文将带你深入了解 多值依赖 和 4NF,帮助你在数据库设计中消除更高级别的异常。 什么是