Jer*_*fin 136
你用三种语言标记了这个,三者之间的答案确实很不一样.对C++的讨论或多或少意味着对C演员的讨论,并且(或多或少)给出了第四个答案.
既然这是你没有明确提到的那个,我将从C开始.C演员阵容有很多问题.一个是他们可以做许多不同的事情.在某些情况下,演员只会告诉编译器(实质上):"闭嘴,我知道我在做什么" - 即,它确保即使你做了可能导致问题的转换,编译器不会警告你这些潜在的问题.例如,char a=(char)123456;
.定义此实现的确切结果(取决于的大小和签名)char
),除了在相当奇怪的情况下,可能没用.C转换也有所不同,它们是否仅在编译时发生(即,您只是告诉编译器如何解释/处理某些数据)或在运行时发生的事情(例如,实际从double转换为长).
C++尝试通过添加许多"新"强制转换运算符来处理这种情况,每个运算符仅限于C强制转换的一部分功能.这使得(例如)意外地进行你真正不想要的转换变得更加困难 - 如果你只打算抛弃一个对象的constness,你可以使用const_cast
,并确保它唯一能影响的是对象是const
,volatile
是否.相反,static_cast
不允许影响对象是否是const
或volatile
.简而言之,您拥有大多数相同类型的功能,但它们被分类,因此一个演员通常只能进行一种转换,其中单个C风格的演员阵容可以在一次操作中进行两次或三次转换.主要的例外是你可以在至少某些情况下使用a dynamic_cast
来代替a static_cast
,尽管被写为a dynamic_cast
,它最终会以a static_cast
.例如,您可以使用dynamic_cast
遍历或向下遍历类层次结构 - 但是层次结构的"向上"始终是安全的,因此可以静态地完成,而在层次结构中"向下"的转换不一定是安全的,因此它是动态完成.
Java和C#彼此更相似.特别是,他们两个铸造(实际上?)总是一个运行时操作.就C++强制转换运算符而言,它通常与dynamic_cast
实际完成的最接近- 即,当您尝试将对象强制转换为某种目标类型时,编译器会插入运行时检查以查看是否允许该转换如果不是,则抛出异常.确切的细节(例如,用于"错误转换"异常的名称)会有所不同,但基本原则仍然大致相似(但是,如果内存服务,Java会将转换应用于少数非对象类型,例如int
更接近C演员阵容 - 但这些类型的使用很少,以至于1)我不记得这是肯定的,2)即使它是真的,无论如何也无关紧要).
更普遍地看待事情,情况非常简单(至少是IMO):演员(显然足够)意味着你正在将某种东西从一种类型转换为另一种类型.当/如果你这样做,它提出了一个问题"为什么?" 如果你真的想要一些特定类型的东西,你为什么不把它定义为开始的那种类型?这并不是说从来没有理由进行这样的转换,但是只要它发生,它应该提示你是否可以重新设计代码以便在整个过程中使用正确的类型.即使看似无害的转换(例如,在整数和浮点之间)也应该比常见的更加密切地进行检查.尽管他们似乎相似性,整数应该用于"计数"类型的事物和浮动点用于"测量"类型的事物.忽视这种区别是导致一些疯狂的陈述,例如"普通美国家庭有1.8个孩子".即使我们都能看到这种情况如何发生,但事实是没有一个家庭有1.8个孩子.他们可能有1或者他们可能有2或者他们可能有更多 - 但从来没有1.8.
Eri*_*ert 46
这里有很多好的答案.这是我看待它的方式(从C#角度来看).
铸造通常意味着两件事之一:
我知道这个表达式的运行时类型,但编译器不知道它.编译器,我告诉你,在运行时,对应于这个表达式的对象实际上就属于这种类型.截至目前,您知道此表达式将被视为此类型.生成假定对象将具有给定类型的代码,或者如果我错了则抛出异常.
编译器和开发人员都知道表达式的运行时类型.还有另一个与此表达式在运行时将具有的值相关联的值的值.生成从给定类型的值生成所需类型值的代码; 如果你不能这样做,那么抛出异常.
请注意,这些是对立的.有两种演员阵容!有一些强制转换,你可以向编译器提供有关现实的提示 - 嘿,类型对象的这个东西实际上是Customer类型 - 并且有一些强制转换,你告诉编译器执行从一种类型到另一种类型的映射 - 嘿,我需要与这个double对应的int.
两种演员阵容都是红旗.第一种类型的演员提出了一个问题"为什么开发人员知道编译器不知道的东西到底是什么?" 如果您处于这种情况,那么更好的做法通常是更改程序,以便编译器确实能够处理现实.那你就不需要演员; 分析在编译时完成.
第二种类型的演员提出了"为什么不首先在目标数据类型中进行操作?"的问题.如果你需要一个ints的结果,那你为什么要先拿一个双?你不应该持一个int吗?
还有一些额外的想法:
http://blogs.msdn.com/b/ericlippert/archive/tags/cast+operator/
Mik*_*ler 35
转换错误始终报告为java中的运行时错误.使用泛型或模板将这些错误转换为编译时错误,使您在出错时更容易检测到错误.
正如我上面所说.这并不是说所有的铸造都很糟糕.但如果有可能避免它,最好这样做.
Ste*_*end 18
铸造本身并不是坏事,只是它经常被滥用作为实现某些事情的手段,这些事情要么根本不应该完成,要么更优雅地完成.
如果它普遍不好,语言就不会支持它.像任何其他语言功能一样,它有它的位置.
我的建议是专注于您的主要语言,并了解其所有演员表和相关的最佳实践.这应该告诉其他语言的短途旅行.
相关的C#文档在这里.
在此前的SO问题中,对C++选项有一个很好的总结.
我主要是在这里讲C++,但大部分内容可能也适用于Java和C#:
C++是一种静态类型语言.语言允许你在这里有一些余地(虚函数,隐式转换),但基本上编译器在编译时知道每个对象的类型.使用这种语言的原因是可以在编译时捕获错误.如果编译器知道类型的a
和b
,那么它会抓住你在编译的时候,你做的a=b
哪里a
是一个复杂的号码,b
是一个字符串.
无论何时进行显式转换,都要告诉编译器闭嘴,因为你认为你知道的更好.如果你错了,你通常只会在运行时发现.在运行时发现的问题是,这可能是客户的问题.
Java,c#和c ++是强类型语言,虽然强类型语言可以被视为不灵活,但它们具有在编译时进行类型检查并保护您免受因某些操作的错误类型而导致的运行时错误的好处.
基本上有两种类型的转换:转换为更一般的类型或转换为其他类型(更具体).转换为更通用的类型(强制转换为父类型)将使编译时检查保持不变.但是转换为其他类型(更具体的类型)将禁用编译时类型检查,并且将由运行时检查替换为编译器.这意味着您不太确定您编译的代码是否可以正确运行.由于额外的运行时类型检查(Java API充满了强制转换!),它也会对性能产生一些微不足道的影响.