Operatörün aşırı yüklenmesi hakkında şikayet eden birçok yayın var.
Bu operatöre alternatif bir bakış açısı sunarak "operatör aşırı yüklenmesi" kavramlarını açıklığa kavuşturmak zorunda olduğumu hissettim.
Kod gizliyor mu?
Bu argüman yanlıştır.
Tüm dillerde gizlemek mümkündür ...
C ++ veya Java'daki kodları işleçler / yöntemlerle gizlemek, C ++ 'da olduğu gibi operatör aşırı yüklemeleriyle gizlemek kolaydır:
// 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'nın standart arayüzlerinde bile
Başka bir örnek olarak, Java'daki Cloneable
arayüzü görelim :
Bu arabirimi uygulayan nesneyi klonlamanız gerekiyor. Ama yalan söyleyebilirsin. Ve farklı bir nesne oluşturun. Aslında, bu arayüz o kadar zayıf ki, sadece eğlenmek için başka bir nesne türünü tamamen geri döndürebilirsiniz:
class MySincereHandShake implements Cloneable
{
public Object clone()
{
return new MyVengefulKickInYourHead() ;
}
}
Gibi Cloneable
arayüz kötüye kullanılabilir / şaşırtma, bu C ++ operatör yükleme olması gerekiyordu aynı gerekçelerle yasaklı olmalıdır?
Günün dizeli saatini döndürmesi için sınıfın toString()
yöntemini aşırı MyComplexNumber
yükleyebiliriz. Meli toString()
aşırı yükleme de yasaklanmalı? MyComplexNumber.equals
Rastgele bir değer döndürmesi, işlenenleri değiştirmesi ... vb. Vs .. için sabotaj yapabiliriz .
Java'da, C ++ veya herhangi bir dilde olduğu gibi, programcı kod yazarken minimum semantiğe saygı göstermelidir. Bu add
, ekleyen bir işlev Cloneable
uygulamak ve klonlayan uygulama yöntemi ve ++
artışlardan daha fazla bir işleç anlamına gelir .
Zaten ne şaşırttı?
Artık kodun bozulmamış Java yöntemleriyle bile sabote edilebildiğini bildiğimize göre, kendimize C ++ 'da aşırı operatörün gerçek kullanımı hakkında soru sorabilir miyiz?
Açık ve doğal gösterim: yöntemler ve operatör aşırı yüklemesi?
Farklı durumlarda, hangi kodlama stilinin daha net olduğu hakkında bir fikre sahip olmak için Java ve C ++ 'daki "aynı" kodu karşılaştıracağız.
Doğal karşılaştırmalar:
// 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 ;
Operatör aşırı yükleri sağlandığı sürece A ve B'nin C ++ 'da herhangi bir türde olabileceğini lütfen unutmayın. Java'da, A ve B ilkel olmadığında, kod ilkel benzeri nesneler için bile (BigInteger, vb.) Çok kafa karıştırıcı olabilir.
Doğal dizi / konteyner erişimcileri ve abonelik:
// 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'da, her bir kapsayıcı için aynı şeyi yapma (içeriğine bir dizin veya tanımlayıcı aracılığıyla erişme) için bunu yapmak için farklı bir yolumuz olduğunu görüyoruz, bu da kafa karıştırıcı.
C ++ 'da, her kapsayıcı, operatörün aşırı yüklenmesi sayesinde içeriğine erişmek için aynı yolu kullanır.
Doğal ileri tip manipülasyon
Aşağıdaki örneklerde Matrix
, " Java Matrix nesnesi " ve " C ++ Matrix nesnesi " için Google'da bulunan ilk bağlantılar kullanılarak bulunan bir nesne kullanılır :
// 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
Ve bu sadece matrislerle sınırlı değil. BigInteger
Ve BigDecimal
Java sınıfları C onların benzerleri ++ gibidir oysa aynı kafa karıştırıcı ayrıntı muzdarip dahili içinde türleri gibi berrak.
Doğal yineleyiciler:
// 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
Doğal fonksiyonlar:
// C++ Functors
myFunctorObject("Hello World", 42) ;
// Java Functors ???
myFunctorObject.execute("Hello World", 42) ;
Metin birleştirme:
// 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") ;
Tamam, Java'da da kullanabilirsiniz MyString = "Hello " + 25 + " World" ;
... Ama bir saniye: Bu operatörün aşırı yüklenmesi, değil mi? Hile değil mi ???
:-D
Genel kod mu?
Aynı genel kod değiştiren işlenenler hem yerleşik / temel öğeler (Java'da arabirimi olmayan), standart nesneler (doğru arabirime sahip olamayan) hem de kullanıcı tanımlı nesneler için kullanılabilir olmalıdır.
Örneğin, rastgele türlerin iki değerinin ortalama değerinin hesaplanması:
// 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.
Operatör aşırı yüklemesi tartışılıyor
Artık, operatör aşırı yüklemesi kullanan C ++ kodu ile Java'daki aynı kod arasında adil karşılaştırmalar gördüğümüze göre, artık "operatör aşırı yüklemesi" ni bir kavram olarak tartışabiliriz.
Operatör aşırı yüklemesi, bilgisayarlardan önce var oldu
Hatta dış bilgisayar biliminin, operatör yükleme vardır: Örneğin, matematik, operatörler gibi +
, -
, *
aşırı yüklü vb.
Gerçekten de, anlam +
, -
, *
vb değişiklikler işlenen türleri (vs rakamsal, vektörler, kuantum dalga fonksiyonları, matrisler,) bağlı olarak değişebilir.
Bilim kurslarımızın bir parçası olarak çoğumuz, işlenen türlerine bağlı olarak operatörler için birçok anlam öğrendik. Onları kafa karıştırıcı bulduk mu?
Operatör aşırı yüklenmesi, operandlarına bağlıdır
Bu, operatörün aşırı yüklenmesinin en önemli parçasıdır: Matematikte veya fizikte olduğu gibi, işlem, işlenen tiplerine bağlıdır.
Yani, işlenenin türünü bilin ve işlemin etkisini bileceksiniz.
C ve Java bile (sabit kodlanmış) operatör aşırı yüklemesine sahiptir
C'de, bir operatörün gerçek davranışı, işlenenlerine göre değişecektir. Örneğin, iki tamsayı eklemek iki kat, hatta bir tamsayı ve bir çift eklemekten farklıdır. İşaretçi aritmetik etki alanının tamamı bile vardır (döküm olmadan, bir işaretçiye bir tamsayı ekleyebilirsiniz, ancak iki işaretçi ekleyemezsiniz).
Java'da, işaretçi aritmetiği yoktur, ancak birileri hala +
operatör olmadan dize birleştirme bulmuş , "operatör aşırı yüklenmesi kötü" inancında bir istisnayı haklı çıkaracak kadar saçma olacaktır.
Sadece bir C (tarihsel nedenlerden dolayı) veya Java ( kişisel nedenlerden dolayı aşağıya bakın) kodlayıcısı olarak kendinizinkini sağlayamazsınız.
C ++ 'da, operatör aşırı yüklenmesi isteğe bağlı değildir ...
C ++ 'da, yerleşik türler için operatör aşırı yüklemesi mümkün değildir (ve bu iyi bir şeydir), ancak kullanıcı tanımlı türlerde kullanıcı tanımlı operatör aşırı yüklemeleri olabilir.
Daha önce de belirtildiği gibi, C ++ 'da ve Java'nın aksine, kullanıcı türleri yerleşik türlerle karşılaştırıldığında dilin ikinci sınıf vatandaşları olarak kabul edilmez. Bu nedenle, yerleşik türlerin operatörleri varsa, kullanıcı türlerinin de sahip olması gerekir.
Gerçek şu ki gibi toString()
, clone()
, equals()
yöntemler Java içindir ( yani yarı-standart-benzeri ), C ++ operatör aşırı yükleme o sözü Java yöntemlerine önce orijinal C operatörleri gibi doğal olarak ya da olur C ++ kadar çok parçasıdır.
Şablon programlama ile birleştirildiğinde, operatör aşırı yüklenmesi bilinen bir tasarım deseni haline gelir. Aslında, aşırı yük operatörleri ve kendi sınıfınız için aşırı yük operatörleri kullanmadan STL'de çok ileri gidemezsiniz.
... ama istismar edilmemeli
Operatör aşırı yüklemesi, operatörün anlambilimine saygı göstermeye çalışmalıdır. Bir +
işleci çıkarmayın ("bir add
işlevde çıkartmayın " veya "bir clone
yöntemde bok dönüşü " de olduğu gibi).
Döküm aşırı yüklenmesi, belirsizliğe yol açabileceğinden çok tehlikeli olabilir. Bu yüzden gerçekten iyi tanımlanmış vakalar için ayrılmalıdır. Gelince &&
ve ||
gerçekten ne yaptığınızı bilmiyorsanız yerli operatörler bu kısa devre değerlendirmesini kaybedersiniz olarak, şimdiye kadar, onları aşırı değil &&
ve ||
tadını çıkarın.
Öyleyse ... Tamam ... Öyleyse neden Java'da mümkün değil?
Çünkü James Gosling şöyle dedi:
Operatör aşırı yüklemeyi oldukça kişisel bir seçenek olarak bıraktım çünkü C ++ 'da çok fazla insanı kötüye kullandım.
James Gosling. Kaynak: http://www.gotw.ca/publications/c_family_interview.htm
Lütfen Gosling'in yukarıdaki metnini Stroustrup'un metinleriyle karşılaştırın:
Birçok C ++ tasarım kararının kökenleri, insanları belirli bir şekilde bir şeyler yapmaya zorlamaktan hoşlanmıyor [...] Genellikle, kişisel olarak sevmediğim bir özelliği yasadışı bırakmak istedim, çünkü sahip olduğumu düşünmedim başkaları hakkındaki görüşlerimi zorlama hakkı .
Bjarne Stroustrup. Kaynak: C ++ Tasarımı ve Evrimi (1.3 Genel Altyapı)
Operatörün aşırı yüklenmesi Java'ya fayda sağlar mı?
Bazı nesneler operatör aşırı yüklenmesinden büyük ölçüde yararlanır (BigDecimal, karmaşık sayılar, matrisler, kaplar, yineleyiciler, karşılaştırıcılar, ayrıştırıcılar vb. Gibi somut veya sayısal türler).
C ++ 'da Stroustrup'un alçakgönüllülüğü nedeniyle bu avantajdan yararlanabilirsiniz. Java'da Gosling'in kişisel tercihi yüzünden mahvoldunuz .
Java'ya eklenebilir mi?
Şu anda Java'da aşırı yükleme yapmamanın nedenleri, iç politika, özelliğe alerji, geliştiricilere güvensizlik (biliyorsunuz, Java ekiplerine musallat görünen saboteurlar ...), önceki JVM'lerle uyumluluk, Doğru bir şartname yazma zamanı vs.
Bu yüzden bu özelliği beklerken nefesinizi tutmayın ...
Ama bunu C # !!!
Evet...
Bu iki dil arasındaki tek fark olmaktan uzak olsa da, bu beni asla eğlendiremiyor.
Görünüşe göre, C # millet, onların "her ilkel bir struct
ve struct
Nesneden türetir" ile ilk denemede doğru anladım.
Kullanılan tanımlanmış operatör aşırı yüklemesine karşı tüm FUD'lara rağmen, aşağıdaki diller bunu destekler: Scala , Dart , Python , F # , C # , D , Algol 68 , Smalltalk , Groovy , Perl 6 , C ++, Ruby , Haskell , MATLAB , Eiffel , Lua , Clojure , Fortran 90 , Swift , Ada , Delphi 2005 ...
Pek çok dil, pek çok farklı (ve bazen karşıt) felsefeye sahip ve yine de hepsi bu noktada hemfikir.
Düşünce için yiyecek ...