从C++到Java,显然没有答案的问题是为什么Java没有包含操作符重载?
Complex a, b, c; a = b + c;不是比Complex a, b, c; a = b.add(c);简单多了吗?
是否有一个已知的原因,不允许运算符重载的有效参数?原因是武断的还是浪费时间的?
- 也可以读取Java为什么不支持运算符重载
- @Zzz,我很难读那篇文章。这是自动翻译的,还是作者的第二语言是英语?我觉得这里的讨论更清楚了。
- 对于那些认为这不是建设性的人来说,这个问题产生了一些我见过的最具建设性的对话。也许这是一个更好的programmers.stackexchange.com的候选人,但有时我认为这是过于轻视更广泛的主题。
- @noname这很简单,只是在精神上插入a,而丢失的artlcles则是一个死赠品,表明此人不是英语母语者或程序员(或像这家伙一样,两者都是:)程序员可以删除文章的原因是,它可以使评论更简短,更适合提供的空间。从那以后,他们就习惯了。我的问题是布局,不知怎么的,我总是在谷歌搜索中点击那个网站。幸运的是,有一个很好的chrome扩展名,它清楚地称为"重新格式化难以阅读的页面"。
- 我看不出OP为什么接受第一个答案的原因。@stackoverflow.com/users/14089/paercebal写的答案非常好。它应该被接受。
- 我希望==操作符对字符串过载。每次我从go、php、ruby、python切换回来时都会收到我的消息。
有很多帖子抱怨操作符过载。好的。
我觉得我必须澄清"操作符重载"的概念,提供关于这个概念的另一种观点。好的。代码模糊?
这个论点是错误的。好的。在所有语言中都有可能混淆…
在C或Java中通过函数/方法混淆代码很容易,因为C++是通过运算符重载来实现的:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| // C++
T operator + (const T & a, const T & b) // add ?
{
T c ;
c.value = a.value - b.value ; // subtract !!!
return c ;
}
// Java
static T add (T a, T b) // add ?
{
T c = new T() ;
c.value = a.value - b.value ; // subtract !!!
return c ;
}
/* C */
T add (T a, T b) /* add ? */
{
T c ;
c.value = a.value - b.value ; /* subtract !!! */
return c ;
} |
即使在Java的标准接口中
再举一个例子,让我们看看Java中的EDOCX1 0接口。好的。
您应该克隆实现此接口的对象。但你可以撒谎。并创建另一个对象。事实上,这个接口太弱了,你完全可以返回另一种类型的对象,只是为了好玩:好的。
1 2 3 4 5 6 7
| class MySincereHandShake implements Cloneable
{
public Object clone ()
{
return new MyVengefulKickInYourHead () ;
}
} |
当EDCOX1 0接口可以被滥用/混淆时,它是否应该被禁止在相同的理由C++运算符重载应该是什么?好的。
我们可以重载MyComplexNumber类的toString()方法,让它返回一天中的字符串化时间。是否也应该禁止toString()过载?我们可以破坏MyComplexNumber.equals,让它返回一个随机值,修改操作数…等。好的。
在Java中,与C++一样,或者任何语言,程序员在编写代码时必须遵守最小的语义。这意味着实现一个添加的add函数,和一个克隆的Cloneable实现方法,以及一个比递增的++运算符。好的。到底是什么让人困惑?
既然我们知道代码可以通过原始Java方法被破坏,我们可以问自己C++中运算符重载的真正用法吗?好的。清晰自然的符号:方法与运算符重载?
下面我们将比较不同的情况,爪哇和C++中的"相同"代码,以了解哪种编码风格更清晰。好的。自然比较:
1 2 3 4 5 6 7 8 9 10 11
| // C++ comparison for built-ins and user-defined types
bool isEqual = A == B ;
bool isNotEqual = A != B ;
bool isLesser = A < B ;
bool isLesserOrEqual = A <= B ;
// Java comparison for user-defined types
boolean isEqual = A.equals(B) ;
boolean isNotEqual = ! A.equals(B) ;
boolean isLesser = A.comparesTo(B) < 0 ;
boolean isLesserOrEqual = A.comparesTo(B) <= 0 ; |
请注意,A和B可以是C++中的任何类型,只要提供了操作符重载。在Java中,当A和B不是基元时,即使对于原始对象(BigTigin,等等),代码也会变得非常混乱。好的。自然数组/容器访问器和订阅:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| // C++ container accessors, more natural
value = myArray[25] ; // subscript operator
value = myVector[25] ; // subscript operator
value = myString[25] ; // subscript operator
value = myMap["25"] ; // subscript operator
myArray[25] = value ; // subscript operator
myVector[25] = value ; // subscript operator
myString[25] = value ; // subscript operator
myMap["25"] = value ; // subscript operator
// Java container accessors, each one has its special notation
value = myArray[25] ; // subscript operator
value = myVector.get(25) ; // method get
value = myString.charAt(25) ; // method charAt
value = myMap.get("25") ; // method get
myArray[25] = value ; // subscript operator
myVector.set(25, value) ; // method set
myMap.put("25", value) ; // method put |
在Java中,我们看到对于每个容器做相同的事情(通过索引或标识符访问它的内容),我们有不同的方式去做,这是令人困惑的。好的。
在C++中,由于运算符重载,每个容器都使用相同的方法访问其内容。好的。自然高级类型操作
下面的例子使用EDCOX1的9个对象,用谷歌上找到的第一个链接找到"Java矩阵对象"和"C++矩阵对象":好的。
1 2 3 4 5 6 7 8 9 10 11 12 13
| // C++ YMatrix matrix implementation on CodeProject
// http://www.codeproject.com/KB/architecture/ymatrix.aspx
// A, B, C, D, E, F are Matrix objects;
E = A * (B / 2) ;
E += (A - B) * (C + D) ;
F = E ; // deep copy of the matrix
// Java JAMA matrix implementation (seriously...)
// http://math.nist.gov/javanumerics/jama/doc/
// A, B, C, D, E, F are Matrix objects;
E = A.times(B.times(0.5)) ;
E.plusEquals(A.minus(B).times(C.plus(D))) ;
F = E.copy() ; // deep copy of the matrix |
这不仅限于矩阵。EDCOX1 10和EDCOX1,11个类Java遭受相同的混淆冗长,而它们在C++中的等价物与内置类型一样清晰。好的。自然迭代器:
1 2 3 4 5 6 7 8 9 10 11 12
| // C++ Random Access iterators
++it ; // move to the next item
--it ; // move to the previous item
it += 5 ; // move to the next 5th item (random access)
value = *it ; // gets the value of the current item
*it = 3.1415 ; // sets the value 3.1415 to the current item
(*it).foo() ; // call method foo() of the current item
// Java ListIterator<E>"bi-directional" iterators
value = it.next() ; // move to the next item & return the value
value = it.previous() ; // move to the previous item & return the value
it.set(3.1415) ; // sets the value 3.1415 to the current item |
自然功能:
1 2 3 4 5
| // C++ Functors
myFunctorObject("Hello World", 42) ;
// Java Functors ???
myFunctorObject.execute("Hello World", 42) ; |
文本连接:
1 2 3 4 5 6 7 8 9
| // C++ stream handling (with the << operator)
stringStream <<"Hello" << 25 <<" World" ;
fileStream <<"Hello" << 25 <<" World" ;
outputStream <<"Hello" << 25 <<" World" ;
networkStream <<"Hello" << 25 <<" World" ;
anythingThatOverloadsShiftOperator <<"Hello" << 25 <<" World" ;
// Java concatenation
myStringBuffer.append("Hello").append(25).append(" World") ; |
好的,在Java中你也可以使用EDCOX1 0。但是,等等:这是运算符重载,不是吗?这不是作弊吗????好的。
-D好的。通用代码?
修改操作数的通用代码应该既可以用于内置的INS/原语(Java中没有接口),也可以使用标准对象(它没有正确的接口)和用户定义的对象。好的。
例如,计算任意类型的两个值的平均值:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| // C++ primitive/advanced types
template<typename T>
T getAverage(const T & p_lhs, const T & p_rhs)
{
return (p_lhs + p_rhs) / 2 ;
}
int intValue = getAverage(25, 42) ;
double doubleValue = getAverage(25.25, 42.42) ;
complex complexValue = getAverage(cA, cB) ; // cA, cB are complex
Matrix matrixValue = getAverage(mA, mB) ; // mA, mB are Matrix
// Java primitive/advanced types
// It won't really work in Java, even with generics. Sorry. |
讨论运算符重载
现在我们已经看到C++代码使用运算符重载和Java中相同的代码进行了公平的比较,现在我们可以讨论"运算符重载"作为一个概念。好的。自从计算机出现之前就存在运算符重载
即使在计算机科学之外,也存在运算符重载:例如,在数学中,像+、-、*等运算符重载。好的。
事实上,+、-、*等的含义根据操作数的类型(数字、矢量、量子波函数、矩阵等)而变化。好的。
我们大多数人,作为我们科学课程的一部分,根据操作数的类型,学习了操作数的多种含义。我们发现他们很困惑吗,他们?好的。运算符重载取决于其操作数
这是运算符重载的最重要部分:就像在数学或物理中一样,操作取决于其操作数的类型。好的。
所以,知道操作数的类型,你就会知道操作的效果。好的。即使C和Java也有(硬编码)运算符重载。
在C语言中,运算符的实际行为将根据其操作数而改变。例如,添加两个整数不同于添加两个双精度数,甚至一个整数和一个双精度数。甚至还有整个指针算术域(不进行强制转换,您可以向指针添加一个整数,但不能添加两个指针…)。好的。
在爪哇中,没有指针运算,但仍有人发现没有EDCX1〔1〕操作符的字符串连接将是可笑的,足以证明"运算符重载是邪恶"信条中的一个例外。好的。
只是作为一个C(由于历史原因)或Java(出于个人原因,见下文)编码器,你不能提供你自己的。好的。在C++中,操作符重载不是可选的…
在C++中,内置类型的运算符重载是不可能的(并且这是一件好事),但是用户定义的类型可以有用户定义的运算符重载。好的。
正如前面已经说过的,在C++中,与Java相反,与内置类型相比,用户类型不被认为是语言的二等公民。因此,如果内置类型具有运算符,那么用户类型也应该能够拥有它们。好的。
事实上,像EDCOX1的0位,EDCOX1,1,EDCX1,2种方法是Java(即准标准),C++运算符重载是C++的一部分,它与原C运算符一样自然,或者前面提到的Java方法。好的。
结合模板编程,运算符重载成为一种众所周知的设计模式。事实上,在STL中,如果不使用重载运算符,也不为自己的类使用重载运算符,就不能走得很远。好的。…但不应滥用
运算符重载应该努力尊重运算符的语义。不要在+运算符中减去(如"不要在add函数中减去",或"在clone方法中返回垃圾")。好的。
强制转换重载可能非常危险,因为它们会导致模棱两可。因此,它们确实应该为定义明确的案例保留。至于&&和||,除非你真的知道你在做什么,否则永远不要让它们过载,因为你会失去本土运营商&&和||喜欢的短路评估。好的。所以…好啊。。。那么为什么Java不可能呢?
因为詹姆斯·戈斯林说过:好的。
I left out operator overloading as a fairly personal choice because I had seen too many people abuse it in C++.
Ok.
James Gosling. Source: http://www.gotw.ca/publications/c_family_interview.htm
Ok.
请将上面的戈斯林文本与下面的斯特劳斯鲁普文本进行比较:好的。
Many C++ design decisions have their roots in my dislike for forcing people to do things in some particular way [...] Often, I was tempted to outlaw a feature I personally disliked, I refrained from doing so because I did not think I had the right to force my views on others.
Ok.
Bjarne Stroustrup. Source: The Desing and Evolution of C++ (1.3 General Background)
Ok.
操作员超载有利于Java吗?
一些对象将从运算符重载(具体或数值类型,如bigdecimal、复数、矩阵、容器、迭代器、比较器、解析器等)中受益匪浅。好的。
在C++中,由于Stroustrup的谦逊,你可以从中受益。在爪哇,因为戈斯林的个人选择,你完全被搞砸了。好的。它可以添加到Java吗?
在Java中不添加操作符重载的原因可能是内部政治、对特性的过敏症、开发人员的不信任(你知道,那些困扰Java团队的破坏者……)、与以前JVM的兼容性、编写正确规范的时间等等。好的。
所以不要屏住呼吸等待这个功能…好的。但他们用C来做!!!!
是啊。。。好的。
虽然这远非这两种语言之间唯一的区别,但这一种语言却总能取悦我。好的。
显然,C_族的"每个原语都是一个struct,一个struct是从物体衍生出来的",他们一开始就做对了。好的。他们用其他语言做的!!!!
尽管所有的FUD都反对使用定义的运算符重载,但以下语言支持:斯卡拉、DART、Python、F*、C、D、Algol 68、SimulalTalk、Groovy、Perl 6、C++、露比、Haskell、Matlab、Effell、Lua、Crojule、Fortran 90、SWIFT、艾达、Delphi 2005…好的。
有那么多语言,有那么多不同的(有时是对立的)哲学,但他们都同意这一点。好的。
思考的食物…好的。好啊。
- 这是一个很好的答案。我不同意,但这仍然是一个很好的答案。我认为错误重载可能导致的问题超过了好重载的值。
- 减法,而不是减法
- 我猜您忘记了在字符串连接的Java示例中有一个"追加"。还忘了字符串中有一两个空格。:)
- @丹尼森:我按照你的建议修改了代码。谢谢你的评论…^ ^…
- @Douglas Leeder:Thanks!运营商的贷款是一样的oop。The first time you learn to do it,you write overloads everywhere as you would put base classes and inheritance everywhere(like,Sweet Irony,the Java Api).但是,这是一个很好的时机,他们欢迎你的可能性,所有的一切,而不是滥用它。My own 10-years+experience about C++is that the number of bad overloads I saw both in my code and code from other coders is so low I believe I could count them on one hand.and this is a lot less than the number of overall bugs with sprintf,strcat,memset and buffer overruns.
- That might take the record for the longest delay for a response to a comment:--
- I now have 7 years of working experience with C++。The only operator overrides I written or interacted with have been stream operator-which are definitely an abuse of shifting operators.On the other hand shift operators for output are rather conventing,and a s a special case,I guess it's acceptable.
- @Douglas Leeder:About the use on the EDOCX1 universal 0 and EDOCX1 original 1.Operators with streams:I don't even remember using them outside the context of a stream(multilying/divising an unsigned integer by 2 should not need its special operator),which explains,i guess,why this is the overloading that bothers me the less.My problem with overloading was more of the"this is bad code"kind.For example,the operator EDOCX1 indicative 2 whose return value is a static variable inside the function,for"最优化purposes",or an incisistant operator EDOCX1 individual 3(the method was const,but returned reference was not).
- @Douglas Leeder:I believe,after discussing about that in another so question,that the gap between"lovers"and"haters"of operator overload is probably caused by a difference in their approach of the Code:"haters"are more"functions are matters",meaning that they expect a function to do one thing,and one thing only.Thus operators should work as designed by the language."Lovors"are more about ` objects should behave',meaning they accept more readily that function(and thus,operators)can change their behaviour according to the type of their parameters.
- Epic Answer这是我读过最多的书之一
- @phlesnel:thanks for the"epic answer"comment!
- 在母体中,你写了字母名称4的C+和字母名称5。我认为这应该是字母6。
- @Tim Van Dalen:You're right.I corrected the code.Thank you for the comment…——没有。
- 实际上,我认为它应该是EDOCX1,因为它是EDOCX1。Also,you can write EDOCX1 niplication 9,which is a lot nicer than EDOCX1.伟大的邮报!
- @Niklas B.:Thanks!我纠正了EDOCX1的音标11As for EDOCX1 sinki,I had decided to let it as EDOCX1 commercial 10 because it is easier to decompose,and thus,understand by Java People(apply the EDOCX1 indiscx1,as in the line before,the GET,the value,and then,the EDOCX1 silic 16 to call the method of the value).
- I was thinking some thing like that was the reason you put it this way.Thanks for the followup!
- Thanks for this,it's a great answer.And I also say'substract'all the time.
- I am actually mad that the questioner did not select this response as the accepted answer.
- @Sokwan Huh:EDOCX1 Filogical 17:There's actually a gold badge for answer s receiving twice the points of the accepted answer:stackoverflow.com/badge s/62/populist…就是这样。
- You know,single line comments(EDOCX1 universal 18)work in C,just like they do in C§35;and Java.
- @Hassan:EDOCX1 theographic 19 original IIRC,single line comments work in C99,not in C89.
- Great answer it should have been left to the programmer the choice of operator overloading in Java.
- 上面给出的例子对诸如+-,算术的那些东西是有意义的。其他的函数很难理解,例如字符串流的<<函数。Java语法可能是冗长的,但是…next(),append()的含义非常明确。这样更容易阅读
- @用户1216838:就像在数学或量子物理学中一样,运算符取决于上下文。在C++中,对于函数的"流"("")使用"<"是非常自然的,没有C++开发人员会反对它们。现在,来到C++的C(或Java)开发人员会感到惊讶,但是,嘿,你正在学习一种新的语言。你应该期待着学习新的方法。至于"AppEnter()",这是非常自然的,他们决定不支持Java字符串,而是重载"+"和"+="…-)
- @paercebal当然依赖于上下文,我只是在为更详细和更清晰的语义语法的好处而争论。而不是查看每个新声明类型的文档。Java还决定为静态字符串使用双引号,而不是为每个字符串声明新的字符串("")。但是即使它与其他的操作符不一致,程序员也不会误解这些操作符的含义。
- @用户1216838:for the benefit of more verbose and more clear meaning syntax:这是一个明显的矛盾。矩阵就是这里最好的例子。看看数学和物理中的实际符号,你会发现,他们没有使用像Java的符号那样的符号,而且,实际上,它们的符号与C++相似。您所争论的是您自己的编码习惯,以及不愿意使用运算符重载。C++开发人员(实际上,其他语言DEVS)也没有同样的局限性。
- 反对票,希望我能把我所有的代表都反对票投进这个答案。我现在正在从另一个平台移植到Java,实际上我正在考虑删除这个项目,因为现在我必须用Adter()子()()和10000000方法重载来对我的代码库中的$** @进行简单化,因为像这样的规则,默认的参数值赋值显然是从土地上任意禁止的。爪哇。
- 我同意也不同意。我想使用运算符重载自己,尤其是使用复数和BigInteger和BigDecimal类。但是我不想看到其他人滥用它。
- @Skiwi:However I wouldn't want to see anyone else abuse it:根据我14年的专业经验,"操作员超载滥用"是一个近乎偏执狂的大肆宣传概念。我预测这种偏执狂将结束Java一天也将得到它(可能是Java结构,围绕Java 10,在2020)。在我当前的工作中,用100 +C/C++开发人员和C代码库快速演化成C++,主要问题是模拟(主要是C风格的Casts)、VAIL *和一个模仿OOP特性的遗留C框架。尽管它现在是可用的并且被明智地使用(大多数是函数),但不是运算符重载。
- 戈斯林博士的引用就足够了,不需要写两页纸的文章。
- @用户7610:两页的文章(只有两页?)是否可以通过技术讨论给出技术示例和演示,并做出合理的技术决策。这里的关键词是"技术"…虽然答案有点脱离上下文,但它提供了比简单引用(我相信这是不相关的)更好的东西,也就是说,对感知到的问题的分析。我想收到的压倒性的选票(包括正面和负面)和评论显示,这个答案作为一个整体被发现比那些复制粘贴一个报价更有趣。
- @我们永远不会知道,因为你的答案是唯一包含这句话的答案。国际海事组织的这一观点表明,人们普遍要求就这一话题展开一场讨论,而不是直接回答。原来的问题是关于Java的,只是因为Java程序员要求它。
- @用户7610:在编辑之前,请阅读原始问题:stackoverflow.com/revisions/77718/1,您会看到里面有多个问题,我回答了它们。这个问题在4年后被简化了,在我做了最后一次非琐碎的编辑之后。
- @白芍足够了。和平。
- 我真不敢相信,在这样一个清晰的逻辑回答之后,人们还是得不到OP的观点!讨论一些琐碎的事情,比如"<<"是否令人困惑。这与OP的推理无关。关键是,你不能因为某些人滥用某些功能而禁止它,任何东西都可能被滥用。如果一把刀是用来切肉的,如果有人用它来杀人,你决定让所有的刀都变钝,变钝,甚至连水果都不会切吗?
- 这完全忽略了一个事实,即开发人员通常不希望+具有与默认不同的实现/含义。像a = b + c这样的东西突然有了许多不同的含义。这也起作用,因为在Java中,事物经常自动转换为字符串(我不是说这是一件好事),所以EDCOX1〔1〕很快就会迷惑。这就是混淆的地方。运算符重载是一种平衡行为,而gosling更倾向于将副作用最小化而不是简洁性。称之为"勾引某人"不仅是错误的,也是虐待。
- @Maartenbodewes:开发者的期望是由语言决定的。在C++中,没有开发人员期望A+B调用内置EDCOX1(3)。相反,他们希望调用正确的operator +。事实上C和Java开发人员对内置操作员有这样的期望,这是由于他们对语言的局限性的了解。然后,出于某种原因,尽管所有其他语言都证明了这一点,但是由于局限性而产生的这种期望却被当作是他们不能没有的东西而被提出。戈斯林的决定是个人爱好的问题。
- @当然,语言设计不是一个民主的过程(幸运的是)。任何一种决定基本上都是"个人品味的问题"。但是,Java的目标是一个可维护的、相对简单的语言(对于用户和编译器/工具生成器,请注意),排除运算符重载是——或者至少完全是Java的精神。流行语言中的每一个构造都可以像您所做的那样得到保护,但是如果您把它们全部放在一起,就会得到D,这是一种主要用来填充所支持特性的所有标记的语言,因此在很大程度上是不可维护的。
- @Maartenbodewes除了戈斯林的"品味决定"学派外,还有斯特劳斯特鲁普的"考虑到你和其他专家,包括那些不同意你的专家的经验,做出决定"学派。我倾向于上一所学校。此外,运算符重载不是一个技术挑战,也不是复杂的理解/同化,如无数的其他语言(包括Java的孪生,C*)启用它。运算符重载只是一种以错误方式摩擦某些人的行为。太糟糕了,因为正如我所演示的,它可以使代码更清晰。
- 抱歉,但是Java是Java的原因很大程度上忽略了开发人员的请求。所有的特征都是可以防御的。把它们全部变成一种语言,你得到C++或D. Java受到设计的限制。它可能需要下一步(比如Kotlin)。但不管讨论的是什么,上面的答案都归咎于戈斯林。我对不同意的人没意见,但把责任推到某人身上只会使讨论变得主观。
- @Maartenbodewes:我上面写的所有例子,以及所有困扰你的是"作为一个开发者,你被拧了,因为戈斯林的个人选择"?请写下你自己的答案,捍卫"你的开发者是愚蠢的,让天才们为你决定你需要什么"的角度。这个讨论毫无意义。
- 在我看来,matlab的操作符重载是被破坏的。所有操作都被调度到最高的父对象,所以a.b.c()被调度到a的实现中!
- 你真的可以把你的答案留在"因为詹姆斯·戈斯林是这么说的",不过读得好。
- 但Java的情况似乎并不一致。例如,foreach循环"operator"被我的Iterable定义"override"了,但是"<"没有被我的Comparable定义覆盖。似乎有点不合逻辑,也许在将来的捷豹路虎可以解决一些问题。
- 我个人喜欢运算符重载,它是一种工具。但我并不是在每一个地方都使用它,因为在每一个地方都使用它是不合适的。像所有的工具一样,在你应该使用的时候使用它,不要在你不应该使用的时候使用。杰姆斯的声明是因为开发者滥用它们而把它们从Java中丢掉,我觉得有点侮辱。
杰姆斯.高斯林把设计Java比作如下:
"There's this principle about moving, when you move from one apartment to another apartment. An interesting experiment is to pack up your apartment and put everything in boxes, then move into the next apartment and not unpack anything until you need it. So you're making your first meal, and you're pulling something out of a box. Then after a month or so you've used that to pretty much figure out what things in your life you actually need, and then you take the rest of the stuff -- forget how much you like it or how cool it is -- and you just throw it away. It's amazing how that simplifies your life, and you can use that principle in all kinds of design issues: not do things just because they're cool or just because they're interesting."
你可以在这里阅读引文的上下文
基本上,运算符重载对于建模某种点、货币或复数的类是很好的。但在那之后,你会很快地用完例子。
另一个因素是C++在开发人员重载运算符中滥用了特性,比如"& & &,",'`',演员运算符,当然还有"新"。将此与通过值和异常相结合所带来的复杂性在例外C++书籍中得到了很好的覆盖。
- 您能提供一个"运算符重载的复杂性结合传递值和异常"的代码示例吗?尽管玩了几年的语言,拥有并阅读了所有关于C++的有效的/特别的书,但是我不能理解你的意思。
- 对詹姆斯·戈斯林有用的东西不会对每个人都有用。他用"有趣的"包装实验来推断"扔掉世界上所有我不需要的东西,这样就没有人可以使用这些东西",这是非常短视的。他显然不知道我需要什么或使用什么。
- @B T:与斯特劳斯鲁普在这个问题上的观点相比,最有启发性的是戈斯林的观点:Many C++ design decisions have their roots in my dislike for forcing people to do things in some particular way [...] Often, I was tempted to outlaw a feature I personally disliked, I refrained from doing so because I did not think I had the right to force my views on others. (B. Stroustrup)。
- @最具启发性的是高斯林的…与斯特劳斯特鲁普相比"。有趣的是,我会主观地认为,这种差异导致了一种语言,C++,广受诟病的,另一种,广受欢迎的Java。也许有个地方可以让专家说"这是个坏主意"。
- @软件猴子:"C++,广受诟病与其他,Java,广受欢迎"这是营销炒作。记住,C++是单独发展的,而Java(和.NET)则是从推土机营销中获利的。对于一个"广受欢迎的语言"来说,Java并不局限于服务器应用,而"广受诟病"(可能是Java开发人员和想要降低代码生产成本的管理者)C++从非常高性能的服务器发展到高性能的游戏,这难道不奇怪吗?[…]
- @软件猴子:[…]我自己的Java/.NET和C++之间的比较不太利于Java/NET。总结一下:在爪哇/.NET中生成质量代码比C++更容易和更快,但是在你想要完美地改进高质量的代码的时候,Java/.NET会阻碍你,而C++将使它变得简单。有关详细信息,请参阅我的答案:stackoverflow.com/questions/145110/c-performance-vs-java-c/&hellip;
- "或者不去做…只是因为它们很简洁?抱歉,詹姆斯·高斯林把话放在嘴里,但这是一个Java设计原则,在使用级别上我不同意。当然,在语言设计和实现级别,因为它使语言维护人员的工作简单得多。
- 我喜欢这个答案。当然,这里的很多人都认为C++给了你更多的灵活性,但是我想让他们去看看一些C++代码,这些代码与奇怪的黑客和语言的"异常"特性结合在一起。如果这些特性一开始不可用,那么代码编写起来也同样容易,但是读取和维护起来要容易得多。
- 哈桑:每种语言都有它的黑客,Java的泛型就是其中一个很好的例子。现在,关于I'd like them to go have a look at some C++ code out there that is hideously put together with weird hacks and"exceptional" features of the language:不好的程序员将编写不好的代码,不管语言如何。试着模仿Java中的函数参数的"通过引用"来得到一个想法。我看过密码,笑得很厉害,很疼。这是高斯林不使用的东西,因此,在爪哇需要可怕的黑客,然而,在C和C++中,以零成本存在。
- @当然,我同意每种语言都有它的黑客。我不是在为一种语言或另一种语言辩护,只是每种语言都有其优点和缺点,使其更适合某些问题。我认为Java是代码重用性的明显赢家,因为它要求程序员遵循相同的组织模式,部分是通过去除语言本身的异常特征。在您的回答中,您说过不应该滥用运算符重载。不幸的是,总会有人滥用这个特性和其他特性,使代码无法维护。
- 哈桑:"运算符重载滥用"的奇怪之处在于,我在职业生涯中只看到了两个实例(C++ 12年),代码也不可维护。…现在,每当有人报告这种虐待时,有人来自C或Java人群,通常无法详细说明他/她亲身经历的一个例子。在我看来,这听起来像是合理化。…现在,关于Java的"简单性",我们同意。谷歌用"javaschools"这个词来理解这个"简单"的真正含义:encrypted.google.com/search?Q= JavaSoul
- PaulCeBaar,但是,这个想法似乎集中在Java的非常具体的缺点上。我读了博客文章,这是谷歌搜索的第一次点击,作者着重于Java的弱点,这使得它不适合某些任务(他给操作系统作为一个例子)。他还提到了MapReduce,尽管有Java实现。不管怎样,我还是同意。Java不是每个工作的正确工具。但我不知道"javaschools"生成程序员有多糟糕,因为我现在正好是一名CS学生。希望你们错了!
- "基本上,运算符重载对于一个为某种点、货币或复数建模的类是很好的。但在那之后,你很快就没有例子了,"约会怎么样?还是Java设计者从来没有想到的几十个可排序的数据类型之一?
- "基本上,运算符重载对于一个为某种点、货币或复数建模的类是很好的。但在那之后,你会很快地用光所有的例子,"你想在任何数据类型上做任何类型的I/O吗?"Java的I/O例程非常丑陋,尤其是抽象层和数据库层。此外,任何要访问第n个项或执行某种键->值映射的容器类都可以很好地使用运算符重载。如果没有操作符重载或默认参数值,我会惊讶于有人使用这种语言,尤其是拥有它的Oracle。
- 关于Java的许多事情源于戈斯林的个人看法,这可能并不总是与现实相符。他拒绝提供无符号byte类型,声称无符号类型会导致问题,而实际上,导致问题的唯一无符号类型是那些大于默认提升类型的类型(当然,byte类型不会是;我认为没有任何pascal程序员在byte成为sig时遇到过问题。奈德)然而,对于运算符重载,我认为像Java这样的基于引用的OOP语言有一个引用比较运算符是很重要的。
- 因此,如果==将用于原始比较和引用比较,这将使大多数其他类型的运算符重载成为问题。当然,Java后来通过允许自动拆箱来搞乱这一区别,但我认为使用EDCOX1 3作为Java的语言不应该支持超载,这是一个合理的哲学原因。
- @paercebal现在不添加操作符重载的一个可能原因是compatibility with the previous JVMs。这带来了一个有趣的问题,即,在不改变JVM的情况下,是否可以实现运算符重载?我断言,它可以是纯语法糖(可能会破坏Java语法,而不是JVM)。
- @ GERT3:IMHO,我的Java 8代码应该产生Java 1.2兼容字节码的模型是哑的,并且是一个巨大的技术债务(例如Java泛型的可笑实现)的原因。我应该能够像在.NET中一样以最低的JVM版本为目标,并允许用户安装该JVM(或更高版本)或不使用我的代码。
- 戈斯林也出于同样的原因遗漏了函数指针,看看我们现在的情况……
- +我是唯一一个真正回答这个问题的人。虽然戈斯林的观点可能是有争议的,但这无疑是Java忽略运算符重载的原因。这绝对是最好的答案,也是公认的答案!
查看boost.units:链接文本
它通过运算符重载提供零开销维度分析。这能清楚多少?
1 2 3 4
| quantity<force> F = 2.0*newton;
quantity<length> dx = 2.0*meter;
quantity<energy> E = F * dx;
std::cout <<"Energy =" << E << endl; |
将实际输出"能量=4 J",这是正确的。
- "如果维护工作变得复杂,究竟在哪里使代码变得模糊?"
假设您想要覆盖a所引用对象的先前值,那么就必须调用一个成员函数。
1 2 3
| Complex a, b, c;
// ...
a = b.add(c); |
在C++中,这个表达式告诉编译器在堆栈上创建三(3)个对象,执行加法,并将临时对象中的所得值复制到现有对象EDCOX1×2中。
然而,在Java中,EDCOX1(4)不为引用类型执行值复制,用户只能创建新的引用类型,而不是值类型。因此,对于名为Complex的用户定义类型,赋值意味着复制对现有值的引用。
请考虑:
1 2 3 4
| b.set(1, 0); // initialize to real number '1'
a = b;
b.set(2, 0);
assert( !a.equals(b) ); // this assertion will fail |
在C++中,复制值,因此比较结果不相等。在爪哇中,operator=执行参考拷贝,因此a和EDCX1〔8〕现在指的是相同的值。因此,比较将生成"equal",因为对象将与自身进行比较。
副本和引用之间的差异只会增加运算符重载的混乱。正如塞巴斯蒂安所提到的,Java和C语言都必须分别处理值和引用等式——EDCOX1×9可能会处理值和对象,但是已经实现了EDCOX1×4 }来处理引用。
在C++中,你应该每次只处理一种比较,这样它就不会那么混乱了。例如,在Complex上,operator=和operator==都在处理值——分别复制值和比较值。
- 仅供参考,我没有问这个问题,我只是编辑了标签。点击我名字上方的时间(现在说‘一小时前’)了解更多信息——看起来像是Rengolin(stackoverflow.com/users/14067/Rengolin)问的问题。
- 很简单,真的…就像python一样,没有重载的赋值。
- 这个答案根本回答不了这个问题。你简直是在抱怨Java使用等号。如果B+C返回一个新的复数,那么A=B+C将是完全有效的,是的,阅读起来要简单得多。即使您想在适当的位置修改a,a.set(b+c)的读取也要简单得多——尤其是当算术运算非常简单时:a.set((ab+bc)/5)或a=a.multiple(b).add(b.multiple(c))。divide(5)。你的选择…
- 或者我猜…不是你的选择,视情况而定
- 在C++中,表达式模板解决了额外拷贝的问题。几乎所有主要的算术库都是出于这个原因使用这种技术的。此外,这并不能解决这个问题,因为a=b+c只是a.foo(b.bar(c))的语法糖,这实际上是问题的初始观察结果。
- 这不是问题的答案。这是某人对Java和C++之间某些差异的推测。
- 查看项目Java OO是一个模块扩展(插件)到Java编译器GITHUBCOM/AMELETEV/JAVAO
Java设计者认为操作员重载是比它更值得的麻烦。很简单。
在一个语言中,每个对象变量实际上是一个引用,运算符重载至少会带来一个不合逻辑的额外危险——至少是一个C++程序员。将这种情况与c s==运算符重载和Object.Equals和Object.ReferenceEquals进行比较(或与它的调用进行比较)。
groovy具有运算符重载,并在JVM中运行。如果你不介意性能冲击(每天都会变小)。它是基于方法名自动生成的。例如,'+'调用'plus(argument)'方法。
- 我希望所有语法重的、带有运算符重载的语言都使用这种技术。我不明白他们为什么要发明一个方法命名和查找的特殊版本。stroustrup在d&ec++中没有提到任何替代方案。C小组采用正确的LINQ语法方法(where ...变为.Where(i => ... )。如果他们对算术运算符也这样做,那么许多事情就会变得简单和强大。Java有一个干净的板条的优势,可以得到正确的(虽然出于宗教原因,它可能永远不会)。
- @我经常注意到,当有复杂的分歧时,人们会把双方的动机都贴上"宗教"的标签。
- @Noah,我可以接受这样一个有限的操作符重载子集,前提是方法名有一个特殊的标记,使它们在视觉上保持不同。类似于为实现"+"ol定义一个uu plus()方法,并远离像强制转换甚至数组下标这样的重载。我不愿意忍受的是C++和C。
- 不是答案。在虚拟机上运行着许多语言。运算符重载本身不应该是切换语言的一个好理由。
我认为这可能是一个有意识的设计选择,迫使开发人员创建名称清楚地传达其意图的函数。在C++开发人员中,操作符的功能往往与给定操作符的普遍接受性质无关,这使得几乎不可能在不查看操作符的定义的情况下确定代码是什么。
- 这是一个无缘无故的断言。我是一个专业的C++开发人员,12年来,我很少遇到这个问题。事实上,我在C++中看到的大多数错误和设计错误都是在C样式代码中(EDOCX1,1,Casts,等等)。
- - 1。您分配的每个变量都是一个符号,就像算术运算符符号一样。无论您是使用短语来命名该变量、单个单词还是单个字母,都是您(或您的团队)的决定。谁说什么有意义,什么没有意义?答案是你,程序员。在纯数学中,矩阵之间的乘法与基础算术中两个数字之间的乘法不同。然而,我们对两种类型的乘法都使用相同的符号。
- @paercebal:不幸的是,断言是正确的。你只需要看看iostreams就可以看到它的运行。值得庆幸的是,大多数开发人员对于为现有的操作符发明新的语义更加谨慎。
- @我不同意。iOStream使用operator <<已经有30年的历史了,而且在视觉上比它的c对应词(my-int<output << my_value ;),我甚至无法在没有得到k&r的情况下描述它的确切功能。我相信答案是错误的,不是因为它是错误的,而是因为它意味着只有操作者才能被错误地使用。用户所说的本质上是我们可以编写一个与加法无关的add函数。但是Java的EDCOX1 OR 5显然使用EDCOX1×4来插入。是否也应该禁止使用add?[…]
- @Benvoigt:…]我甚至没有提到add函数可能真的被误用的事实(比如做一个乘法,或者获得一个互斥函数)。USER 14128提到的滥用不限于操作员,但对于操作员超载有某种病理性的恐惧,我相信这是从C和C++的早期开始的,这种恐惧在Java中没有被修改,但幸亏没有进入C…最后,尊重语义和编写清晰的函数/运算符是开发人员的工作。不是语言。
- @parcebal:不知道<<是一个没有副作用的位移位操作符,你已经证明了这一点。这不是"C对应",也就是C++的含义。使用<<进行流插入失败得很惨,因为它具有位移动的优先权,这对于插入来说是非常错误的。这会导致比流的流畅函数调用语法更多的错误。
- @benvoigt如果<<被当作错误的先例,那么代码通常会产生编译时错误(例如cout << 5&3 << endl无法编译)。我要注意的是,在C++中没有太多的操作符会导致这种错误(例如按位、关系、逻辑、赋值、三进制),任何失败的代码都会变得非常疯狂。我认为,在这种情况下,EDCOX1〔10〕应该写为EDCOX1〔11〕,以进一步说明这是一个要输出的项目,而Java的I/O例程证明了操作员重载必须具备干净、简洁、可读的代码。
- 例如:"jbo5112 cout << f() || g();parentheses:《Don’t让clearer,他们使它正确。该位移位操作和它不可以被滥用,他们将是不必要的。为什么cout << (5&3) << endl;比cout.fmt(5&3)(endl);吗?利用函子的成员函数调用操作符在线将是一infinitely变流比更好的设计repurposing位算子只是因为字形的外观漂亮。但这是唯一的一个与远的错误流。
- 答案是正确的,一些C + +开发人员的工作被一个参数特征;这是对常规的工作,这些开发商对标准的不安,对有问题的功能。(这是erred在Java,但它的"力场以上的开发商为所应为"与C++完成的重点控制)
- "benvoigt cout << f() || g();的cout << f(); cout || g();是相同的。除非你过重的ostream类,你可以使用这样的| |算子与返回的类型(G),然后,将无法编译。的原因是,cout.fmt(5&3)(endl);cout << (5&3) << endl;是比第一consistently使用相同的功能,而第二调用的函数(这是系统操作员(罗嗦),然后(我)的对象(如std::_ Endl.))。
- 如果使用C + +"benvoigt算子(算子)而<<()函数更好,那就没在看漂亮的费用。一种呼叫cout("hello")(name);can get to丑女和慢型,尤其是当他们在一起stringing 350输出到创建一个单一的线(如长CSV记录)。利用算符(),cout,"hello",name;可能是更好的,但cout,c1,',',c2,',',c3,',',c4...得到的小混乱。
- "jbo5112:我觉得你周围的对象>转换为。
- "benvoigt Java的解决方案仍然是不是开发商的名称创建函数,其力地传达他们的意图。你仍然是免费的public int add(int num1, int num2) {return ((num1*num2/3)^0xFAFAFAFA);}写功能。
- "jbo5112:我认为你明白我的立场。任何语言可以被滥用,这样做是不是坏的。C++操作符重载的经典流可以被滥用,它已经表明,(在相同的方式是让你arguing)。这是不是意味着不符的重载将有更好的选择。我是我自己的"最thankfully开发商更多的是关于新circumspect inventing语义的算子是存在的。"但我盼望从标准化到一个替代的经典流,是更多的比在许多方面只是运营商滥用。
- "benvoigt你是正确的。是我忽略的自动施放。我要尽量避免大部分的趋势,只为我用的慢。如果g()将返回bool铸造到运行,那就没希望,但我的"M(G)是一个句柄失败至少一码的输出操作。对不起,你是misreading distaste of iostream的bitshift重载的Java agreeing算子的设计与选择。我想我的头仍然包裹在詹姆斯高斯林是如此愚蠢到知识库的一些inane创作力(例如,Java接口的任何数学图书馆),和willingly人仍然选择的语言。
- 看到的性质,只是在来回,包括clarifications和reclarifications,足够让我运行的是从运营商的快速重载的,我可以。
好吧,你真的可以在操作人员过载的情况下把自己踩到脚上。就像用指针一样,人们会犯愚蠢的错误,所以决定把剪刀拿走。
至少我认为这就是原因。不管怎样,我站在你这边。:)
- 但指针:
- 比如这个愚蠢的错误…
- 这是一种非常糟糕的思考方式。你可以开枪打自己的脚,我们宁愿把你的手砍下来,这样你就不能。当然,我们认为你是个会自杀的白痴。
从技术上讲,每种编程语言中都存在运算符重载,可以处理不同类型的数字,例如整数和实数。说明:术语重载意味着一个函数只有几个实现。在大多数编程语言中,运算符+提供了不同的实现,一个用于整数,一个用于reals,这称为运算符重载。
现在,许多人发现Java有操作符重载给操作符+添加字符串是很奇怪的,从数学的观点来看,这确实很奇怪,但是从编程语言的开发者的角度来看,对于操作员+为其他类添加Buffin运算符重载没有什么错。字符串。但是,大多数人都同意,一旦您为字符串添加了内置的+重载,那么通常最好也为开发人员提供此功能。
A完全不同意这样一种谬论,即运算符重载会混淆代码,这是留给开发人员决定的。这是Na?我得想想,老实说,它变老了。
+ 1,用于在Java 8中添加运算符重载。
- Java使用EDCOX1〔2〕来连接任何字符串ISH是非常可怕的,就像在C和FORTRAN中重载EDOCX1的3个部分一样,对于整体和分数划分而言。在许多版本的pascal中,对任何数值类型使用算术运算符都会产生与将操作数强制转换为Real相等的数值结果,尽管可能不是整数的结果必须通过Trunc或Round才能分配给整数。
有人说,Java中的运算符重载会导致混淆。那些人是否曾经停下来查看一些Java代码,做一些基本的数学运算,比如用BigDimple增加百分比的财务价值?…这样一个练习的冗长内容成为它自己的陈词滥调的证明。具有讽刺意味的是,向Java添加运算符重载将允许我们创建自己的货币类,这样可以使这种数学代码优雅而简单(不太模糊)。
如果说运算符重载会导致类型为该运算符与操作逻辑不匹配的逻辑错误,这就像什么都不说一样。如果函数名不适合操作逻辑,也会出现同样类型的错误-那么解决方法是:放弃函数使用的能力!?这是一个滑稽的回答——"不适合操作逻辑",每个参数名、每个类、函数或任何逻辑上不合适的东西。我认为这个选项应该可以用受人尊敬的编程语言来使用,而那些认为它不安全的人——嘿,没有人说你必须使用它。让我们用C。它们的指针是下垂的,但嘿-有"不安全的代码"的声明-程序,你喜欢自己的风险。
假设Java是实现语言,那么A、B和C都将是对具有初始值NULL的类型复杂的引用。另外,假设复数是不可变的,如前面提到的biginteger和类似的不可变bigdecimal,我想您的意思是,当您将引用分配给从添加b和c返回的复数,而不是将此引用与a进行比较时。
Isn't :
1
| Complex a, b, c; a = b + c; |
much simpler than:
1
| Complex a, b, c; a = b.add(c); |
- 我是吗?一个均值;)都可以分配或分配的比较,但总是与= = =是一永远的比较。名称的来源可以介绍自己的大错误。
有时,最好是让操作符重载、友元类和多个继承。
但我仍然认为这是一个好决定。如果Java会有操作符重载,那么我们就不可能在不查看源代码的情况下确定运算符的含义。目前这是不必要的。我认为您使用方法而不是运算符重载的示例也是非常可读的。如果你想让事情变得更清楚,你可以在多毛的陈述上面添加评论。
1 2
| // a = b + c
Complex a, b, c; a = b.add(c); |
- 当然,正如其他地方提到的,您也永远无法确定add函数的含义。
- 是的,我仍然感到欣慰的是,至少我的操作者是硬编码的。当然,拥有这些特性并明智地使用它们只会对我们有好处。问题是,很难知道是否有人明智地使用了它们。你同意理智的定义。-)
- 为澄清代码而添加的注释是代码在支持运算符重载的语言中的外观。此外,注释是用运算符编写的,这一事实掩盖了您对运算符重载的反对。
Java运算符重载的本机支持的替代方案
由于Java没有操作符重载,这里有一些可供选择的替代方案:
使用其他语言。Groovy和斯卡拉都有操作符重载,并且都是基于Java的。
使用Java OO,一个可以在Java中实现操作符重载的插件。请注意,它不是平台独立的。此外,它还有许多问题,与Java的最新版本(即Java 10)不兼容。(原始stackoverflow源)
使用JNI、Java本机接口或替代方案。这允许你写C或C++(也许其他的?)在Java中使用的方法。当然,这也不是平台独立的。
如果有人知道其他人,请发表评论,我会将其添加到此列表中。
这不是不允许的好理由,而是一个实际的理由:
人们并不总是负责任地使用它。从python库scapy中看这个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| >>> IP()
<IP |>
>>> IP()/TCP()
<IP frag=0 proto=TCP |<TCP |>>
>>> Ether()/IP()/TCP()
<Ether type=0x800 |<IP frag=0 proto=TCP |<TCP |>>>
>>> IP()/TCP()/"GET / HTTP/1.0
"
<IP frag=0 proto=TCP |<TCP |<Raw load='GET / HTTP/1.0
' |>>>
>>> Ether()/IP()/IP()/UDP()
<Ether type=0x800 |<IP frag=0 proto=IP |<IP frag=0 proto=UDP |<UDP |>>>>
>>> IP(proto=55)/TCP()
<IP frag=0 proto=55 |<TCP |>> |
解释如下:
The / operator has been used as a composition operator between two
layers. When doing so, the lower layer can have one or more of its
defaults fields overloaded according to the upper layer. (You still
can give the value you want). A string can be used as a raw layer.