你需要弄清楚它是如何工作static_cast, dynamic_cast的,实际上是任何cast一个系列的演员表,但尤其是这 2 个,以至于以后没有帮助去思考cast。
据我所知,static_cast它需要一点一点地(或者它仍然是逐字节地?)将一种类型的数据复制到另一种类型的数据中。看起来很简单,但我仍然对实现感兴趣。如何找到发布?在 Visual Studio 中,我无法导航goToDeclaration/defenition到此函数主体的说明。* _cast 是一般的函数还是什么?它看起来像某种模板函数或C#中的泛型。
dynamic_cast. 验证转换。也就是在他的理解范围内,他允许你做upCast/downCas,但不允许你做crossCast。在这里我有一个大问题:为什么要进行 upCast - 很明显多态性对此起作用。但是downCast为什么在 C++ 中需要它呢?我试图通过阅读 C# 中有类似问题的帖子在stackowerflow上找到答案,但我仍然无法在全局 c# 概念中思考,因为我不太了解。
您还可以将一种类型的对象转换为另一种类型的对象 ( crossCast)。好吧,这对我来说通常是一片茂密的森林。第一:可以以某种方式给出指针,但总的来说,它将如何工作?为此,您需要了解方法调用的深度通常是如何工作的(我知道早期绑定和后期绑定)。
其次:为什么这是必要的?你能举一个更简单的例子吗?
第三:我知道在将一个对象转换为另一个对象之后,字段会混淆并且包含垃圾。由于没有关于如何投射的信息,据我所知,课堂上没有这样的信息。好吧,这些方法通常不清楚它们将如何工作。
C++ 中的转换都不是“按位”或“逐字节”工作的。这就是为什么它们被称为转换。转换将数据从一种格式转换为另一种格式(通常很重要)。一般来说,从“按位”的角度来看,这种转换结果的表示与原值的表示无关。
按位/按字节复制由函数处理
memcpy,而不是转换。static_cast- 也许是最多才多艺的种姓。它可以进行算术转换、指针到/从类型void *的转换、对象指针在层次结构上下的转换、指向成员的指针在层次结构上下的转换,等等。实施什么?所有转换都是语言运算符,因此在语言核心级别实现。
这里有一些术语上的混淆。相反,
dynamic_cast它是唯一可以交叉铸造的演员。交叉转换是在一个公共包含对象中从一个基本子对象到另一个基本子对象的直接转换。那些。交叉转换只能发生在多重继承的情况下也
dynamic_cast知道如何通过虚拟继承向下转型(这是它做不到的static_cast)dynamic_cast旨在用于非平凡的多态转换,但仅在多态类的连贯层次结构内。当然,它dynamic_cast可以检查向下转换和交叉转换的正确性。在许多生活情况下,不可能避免沮丧,尽管从书呆子的角度来看,沮丧可能违背纯 OOP 的原则。在许多情况下,向下转换代码比使用顽固的向下转换避免实现的相同功能更简单。
一个典型的向下转换的惯用例子是Curiously Recurring Template Pattern。
在无关的不相关类型之间进行转换不是,也不称为交叉转换。
只能在不相关的类型之间进行转换
reinterpret_cast,但语言几乎不保证这种转换的结果及其有用性。因此,您可以不带对象本身,而只带指向它们的指针或链接。这样的转换确实且仅
reinterpret_cast. 是的,当通过此类给定的指针或链接进行访问时,结果会变得毫无意义。但是该语言仍然禁止通过此类指针爬入数据 - 行为未定义(极少数例外)。所以它是“有罪”系列的功能。所有这些变换都是特殊函数(有些人称它们为“魔法”)。这些是编译器级函数。所以,如果里面的东西很有意思,就需要把编译器的源码拿出来研究一下。合乎逻辑的假设是,在工作室的情况下,几乎没有人会给他们 :)
但是怎么办?并且您需要查看生成的代码。工作室有机会观看它,但如果您不仅仅局限于工作室,那么您可以使用精彩的网站 - https://gcc.godbolt.org/
我做了一些实验,通常所有的代码都只是将一个指针从一个变量复制到另一个变量,或者编译器意识到我正在尝试转换两个不兼容的类型并拒绝编译。
c upCast 你自己想出来了。但是使用 downCast 就更难了。通常,它的使用是糟糕设计的标志。但是......有时截止日期很紧迫,客户希望在昨天得到它。如果在某些通用函数(接收指向祖先对象的指针)中您想调用后继函数的特定函数,就会出现这种情况。
在这种情况下没关系。但是我到处都听到“按位复制”或“按位相同”。显然是因为位 - 作为信息的最小单位早已为人所知。而且大家都习惯了。而且我觉得这个字节出现的比较晚,它的大小不是常数(虽然C++标准说一个字节严格来说是8位)。
谁知道。您需要查看对象的内部结构和编译器生成的代码。类变量的一切都很清楚——在大多数情况下,它们是通过偏移量来解决的。有了非虚函数,一切也就简单了。但是对于 virtual ... 为此,他们通常想出了 UB 这个词——未定义的行为。在这种情况下会发生什么 - 是的,任何事情。它可能会起作用,也可能会炸毁计算机。
程序员有可怕的幻想。
static_cast 实质上调用适当的转换运算符或构造函数。对于内置类型,事情可能会稍微复杂一些,但原理是一样的。如果你写
然后将隐式调用 int 到 double 转换运算符,同样可以用
double d = static_cast<double>(i). static_cast 仅在相应的转换函数已经存在(作为构造函数或特殊转换运算符)时才有效,并且与这些转换函数完全相同。自然,在简单的情况下,一切都尽可能优化,但很少有简单的按位复制。可以在此处找到哪些转换对内置类型有效。dynamic_cast 所做的只是将基类指针/引用转换为子指针/引用,或者如果对象不是子对象则抛出异常。如果对象根本不是这个基类的后代,即 无论运行时值如何,都不会发生这种转换,将发生编译时错误。反向转换是隐式完成的,或者可选地通过 static_cast 完成。
reinterpret_cast 将指向一种类型的对象的指针解释为指向另一种类型的对象的指针,无论它有多正确。因此,在一般情况下,这种解释是不正确的,会导致未定义的行为(即问题)。对于内置类型,可以将 reinterpret_cast 视为按位复制。与将执行正确转换的 static_cast 不同,reinterpret_cast 不会;如果将 double 强制转换为 uint_64,则结果为未知值(取决于 double 和 uint_64 的硬件表示)。但是,例如,您可以将 uint_64 值转换为指针,反之亦然,但即便如此,它也只能在 64 位系统上工作。
UPD:对于这样的事情,查看编译器最终将代码翻译成什么通常很有用,为此使用https://gcc.godbolt.org/很方便(只记得优化器和相应的编译标志).
编辑:dynamic_cast 在运行时通过从虚函数表或类似表中确定对象的实际类型来工作。为此,指针转换为的类型必须继承自原始指针的类型,或者与其相同。在其他情况下,这种转换原则上是不可能的,因此错误发生在编译阶段。例如,您不能将 int* 转换为 double* 或将 std::runtume_error* 转换为 istream*,无论它们实际指向什么类型的对象。