许多人认为C ++中的运算符重载是一个坏东西(tm),并且是一个错误,不要在较新的语言中重复出现。当然,这是设计Java时专门删除的一项功能。
既然我已经开始阅读Scala,我发现它看起来非常类似于运算符重载(尽管从技术上讲,它没有运算符重载,因为它没有运算符,只有函数)。但是,它似乎与C ++中的运算符重载在质量上没有什么不同,在C ++中,我记得运算符被定义为特殊函数。
因此,我的问题是什么使Scala中定义“ +”的想法比C ++中的想法更好?
许多人认为C ++中的运算符重载是一个坏东西(tm),并且是一个错误,不要在较新的语言中重复出现。当然,这是设计Java时专门删除的一项功能。
既然我已经开始阅读Scala,我发现它看起来非常类似于运算符重载(尽管从技术上讲,它没有运算符重载,因为它没有运算符,只有函数)。但是,它似乎与C ++中的运算符重载在质量上没有什么不同,在C ++中,我记得运算符被定义为特殊函数。
因此,我的问题是什么使Scala中定义“ +”的想法比C ++中的想法更好?
Answers:
C ++从C继承了真正的蓝色运算符。我的意思是6 + 4中的“ +”非常特殊。例如,您无法获得指向该+函数的指针。
另一方面,Scala没有这种方式的运算符。它在定义方法名称方面具有极大的灵活性,并且为非单词符号提供了一些内置的优先级。因此,从技术上讲,Scala没有运算符重载。
不管您要调用它什么,即使在C ++中,运算符重载也不是很坏。问题是当不良的程序员滥用它时。但坦率地说,我认为,剥夺程序员滥用操作员重载的能力并不能解决所有程序员可能滥用的问题。真正的答案是指导。 http://james-iry.blogspot.com/2009/03/operator-overloading-ad-absurdum.html
尽管如此,C ++的运算符重载与Scala的灵活方法命名之间存在差异,恕我直言,这使Scala的可滥用性降低了。
在C ++中,获取固定中标记的唯一方法是使用运算符。否则,必须使用object.message(argument)或pointer-> messsage(argument)或function(argument1,arguments2)。因此,如果您希望代码具有某种DSLish样式,则使用操作符会有压力。
在Scala中,您可以在发送任何消息时获得中缀符号。“对象消息参数”是完全可以的,这意味着您无需使用非单词符号即可获得中缀表示法。
C ++运算符重载实际上仅限于C运算符。加上只能使用运算符的限制,这给人们施加了压力,他们试图将各种不相关的概念映射到相对较少的符号上,例如“ +”和“ >>”
Scala允许使用大量有效的非单词符号作为方法名称。例如,我有一个嵌入式的Prolog式DSL,您可以在其中编写
female('jane)! // jane is female
parent('jane,'john)! // jane is john's parent
parent('jane, 'wendy)! // jane is wendy's parent
mother('Mother, 'Child) :- parent('Mother, 'Child) & female('Mother) //'// a mother of a child is the child's parent and is female
mother('X, 'john)? // find john's mother
mother('jane, 'X)? // find's all of jane's children
:-,!,?和&符号定义为普通方法。仅在C ++中,&将是有效的,因此尝试将此DSL映射到C ++将需要一些已经引起了非常不同概念的符号。
当然,这也使Scala遭受了另一种滥用。在Scala中,可以根据需要命名方法$!&^%。
对于像Scala这样可以灵活使用非单词函数和方法名称的其他语言,请参阅Smalltalk,其中像Scala一样,每个“运算符”都只是另一种方法,而Haskell允许程序员定义灵活命名的优先级和固定性。功能。
int main() {return (3).operator+(5);}
导致error: request for member ‘operator+’ in ‘3’, which is of non-class type ‘int’
许多人认为C ++中的运算符重载是一件坏事(tm)
只由无知。在像C ++这样的语言中,它是绝对必需的,并且值得注意的是,其他开始以“纯粹”的观点开始使用的语言,一旦设计人员发现了必要性,便添加了它。
在C ++中,从来没有普遍认为运算符重载是一个坏主意-只是滥用运算符重载被认为是一个坏主意。一个人实际上并不需要某种语言中的运算符重载,因为无论如何它们都可以用更多冗长的函数调用来模拟。避免Java中的运算符重载使Java的实现和规范更加简单,并且迫使程序员不要滥用运算符。Java社区中有一些关于引入运算符重载的争论。
Scala中的运算符重载的优缺点与C ++相同-如果适当地使用运算符重载,则可以编写更自然的代码-如果不使用,则可以编写更模糊,混淆的代码。
仅供参考:运算符未在C ++中定义为特殊函数,它们的行为与其他函数相同-尽管名称查找存在一些差异,是否需要成为成员函数,以及可以通过两种方式调用它们: ),运算符语法和2)运算符功能ID语法。
add(2, multiply(5, 3))
?
本文-“ C ++和Java的积极遗产 ”-直接回答您的问题。
“ C ++同时具有堆栈分配和堆分配,并且您必须使运算符重载以处理所有情况,而不会导致内存泄漏。确实很难。但是,Java具有单一的存储分配机制和垃圾收集器,这使得运算符重载变得微不足道”。 ..
Java错误地(根据作者)省略了运算符重载,因为它在C ++中很复杂,但是却忘记了原因(或没有意识到它不适用于Java)。
幸运的是,像Scala这样的高级语言为开发人员提供了选择,同时它们仍在同一JVM上运行。
运算符重载没有任何问题。实际上,不具有运算符重载数字类型。(看一些使用BigInteger和BigDecimal的Java代码。)
但是,C ++具有滥用功能的传统。一个经常被引用的例子是,移位操作符被超载以进行I / O。
=
代替,<<
并且>>
遇到了问题,因为它没有正确的运算符优先级(即,它寻找左或右的第一个参数)。因此,他的双手在他可以使用的东西上受了一些束缚。
运算符重载并不是您真正经常需要的东西,但是在使用Java时,如果碰到了真正需要它的地方,它将使您想要撕掉指甲,只是您有借口停止打字。
您刚刚发现的代码会长时间溢出?是的,您必须重新输入全部内容才能与BigInteger一起使用。没有什么比不得不重新发明轮子来更改变量类型更令人沮丧的了。
盖伊·斯蒂尔(Guy Steele)认为,运算符重载也应该用Java编写,他在主题演讲“成长一种语言”中-有一个视频和它的转录,这确实是一个了不起的演讲。您会想知道他在前几页中在谈论什么,但是如果您继续阅读,您将了解重点并获得启发。而且他完全可以发表这样的演讲,这一事实也令人惊讶。
同时,本次演讲激发了许多基础研究的灵感,其中可能包括Scala-这是每个人都应该阅读以从事该领域工作的那些论文之一。
回想一下,他的示例主要涉及数字类(例如BigInteger和一些怪异的东西),但这不是必需的。
但是,如果您不花一点时间就尝试阅读代码,那么滥用操作符重载会导致可怕的结果,甚至正确使用也会使事情变得复杂。但这是个好主意吗?OTOH,这样的库难道不应该为他们的操作员包括操作员备忘单吗?
我相信每个答案都错过了这一点。在C ++中,您可以重载所有想要的运算符,但不能影响对其进行求值的优先级。Scala没有这个问题,IIRC。
至于这是一个坏主意,除了优先级问题外,人们还给操作员带来了真正愚蠢的含义,而且它很少有助于可读性。对于每次都要记住的愚蠢符号来说,Scala库特别糟糕,库维护人员将头埋在沙子里说:“您只需要学习一次”。太好了,现在我需要学习一些“聪明的”作者的神秘语法*我想使用的库数。如果有一个约定,总是提供识字版本的运算符,那并不会太糟糕。
运算符重载不是C ++的发明-它来自Algol IIRC,甚至Gosling都不认为这是一个坏主意。
正如其他答案所指出的;操作符重载本身并不一定是坏事。以某种方式使结果代码变得不明显时,有什么不好呢?通常,在使用它们时,您需要使它们执行最不令人惊讶的事情(使用operator +做除法会对合理类的使用造成麻烦)或如Scott Meyers所说:
客户已经知道int等类型的行为,因此在合理的情况下,您应努力使您的类型以相同的方式运行... 如有疑问,请按int的方法进行操作。(摘自有效的C ++ 3rd Edition项目18)
现在,有些人通过boost :: spirit之类的方法将运算符重载达到了极限。在这个级别上,您不知道它是如何实现的,但是它提供了一种有趣的语法来完成您想要的工作。我不确定这是好是坏。看起来不错,但是我还没用过。
我从未见过一篇文章声称C ++的运算符重载是不好的。
用户可定义的运算符使该语言的用户可以更轻松地实现更高的表达性和可用性。