Operatör aşırı yüklenmesine karşı argümanları anlamıyorum [kapalı]


82

Joel'in yazdığı yazılardan birini okudum :

Genel olarak, bazı şeyleri gizleyen dil özelliklerinden biraz korktuğumu itiraf etmeliyim . Kodu gördüğünde

i = j * 5;

… C'de, en azından, j'nin beşle çarpıldığını ve sonuçların i'de depolandığını biliyorsunuz.

Ama aynı kod parçasını C ++ 'da görürseniz, hiçbir şey bilmiyorsunuzdur. Hiçbir şey değil. C ++ 'da gerçekte neler olduğunu bilmenin tek yolu, i ve j türlerinin ne olduğunu, başka bir yerde bildirilebilecek bir şeyi bulmaktır. Bunun nedeni, j'nin operator*aşırı yüklenmiş bir türde olması ve çoğaltmaya çalıştığınızda çok esprili bir şey yapmasıdır.

(Vurgu benim.) Şeyleri gizleyen dil özelliklerinden korkuyor musun? Bundan nasıl korkarsın? Nesnelere yönelik programlamanın temel fikirlerinden birini gizlemek ( soyutlama olarak da bilinir ) değil mi? Ne zaman bir yöntem çağırırsan a.foo(b), neler yapabileceği hakkında hiçbir fikrin yok. Ne tür ave ne bolduğunu, başka bir yerde bildirilebilecek bir şeyi bulmanız gerekir. Öyleyse, programcıdan çok fazla şey gizlediği için nesne yönelimli programlama yapmalı mıyız?

Ve operatörün aşırı yüklenmesini desteklemeyen bir dilde yazmanız gerekebilecek j * 5olandan farklı j.multiply(5)olan nedir? Yine, yöntemin türünü jve gözünü bulmak zorunda kalacaksınız multiply, çünkü bakalım, çok esprili bir şey yapan jbir multiplyyönteme sahip bir tür olabilir .

“Muahaha, ben bir metodu adlandıran şeytani bir programcıyım multiply, fakat aslında yaptığı şey tamamen belirsiz ve sezgisel değil ve kesinlikle çoğaltan şeyle hiçbir ilgisi yok.” Bir programlama dili tasarlarken göz önüne almamız gereken bir senaryo mu? O zaman tanımlayıcıları yanıltıcı olabileceği gerekçesiyle programlama dillerinden çıkarmak zorundayız!

Bir yöntemin ne yaptığını bilmek istiyorsanız, belgelere göz atabilir veya uygulamanın içine göz atabilirsiniz. Operatör aşırı yüklenmesi sadece sözdizimsel bir şekerdir ve oyunu nasıl değiştirdiğini anlamıyorum.

Lütfen beni aydınlat.


20
+1: İyi yazılmış, iyi tartışılmış, ilginç bir konu ve tartışmalı. P.se sorusunun parlayan bir örneği.
Allon Guralnek

19
+1: İnsanlar Joel Spolsky'yi dinliyor, çünkü iyi yazıyor ve tanınıyor. Ama bu onu% 100 doğru yapmaz. Tartışmana katılıyorum. Hepimiz Joel’in mantığını burada izlersek, asla bir yere gidemeyiz.
Kimse

5
Ben ve j'nin yerel olarak bildirildiğini, böylece türlerini hızlı bir şekilde görebildiğinizi ya da değişken değişken isimleri olduğunu ve uygun şekilde yeniden adlandırılmaları gerektiğini savunuyorum.
Cameron MacFarland

5
+1, ama Joel'in makalesinin en iyi kısmını unutma: doğru cevaba doğru maraton koştuktan sonra, açık bir sebep olmadan 50 metre kısa durur. Yanlış kod sadece yanlış görünmemeli; derlememeli.
Larry Coleman,

3
@Larry: Sınıfları uygun şekilde tanımlayarak yanlış kodun derlenememesini sağlayabilirsiniz, bu nedenle örneğinde C ++ 'ta SafeString ve UnsafeString'e veya RowIndex ve ColumnIndex'e sahip olabilirsiniz, ancak operatörlerin aşırı yüklenmesini sezgisel olarak davranmaları için kullanmak zorunda kalırsınız.
David Thornley

Yanıtlar:


32

Soyutlama, kodu 'gizler', bu yüzden içsel işleyiş konusunda endişelenmenize gerek kalmaz ve sık sık onları değiştiremezsiniz, ancak amaç bakmanıza engel olmak değildi. Operatörler hakkında sadece varsayımlarda bulunuyoruz ve Joel'in dediği gibi, her yerde olabilir. Tüm aşırı yüklenmiş operatörlerin belirli bir yerde kurulmasını gerektiren bir programlama özelliğine sahip olmak, onu bulmakta yardımcı olabilir, ancak onu kullanmayı daha kolay hale getirdiğinden emin değilim.

Veriyi silen Get_Some_Data adlı bir işlevden daha iyi çarpıma benzemeyen bir şey yapmanın * yapıldığını görmüyorum.


13
+1 'Göremiyorum' biti için. Dil özellikleri suistimal değil, kullanım içindir.
Michael K,

5
Yine <<de, standart C ++ kütüphanesinde, bitsel kayma ile ilgisi olmayan akışlarda tanımlanmış bir operatörümüz var .
Malcolm,

'Bitsel kaydırma' işleci sadece tarihsel nedenlerden dolayı denir. Standart türlere uygulandığında, bit cinsinden bir kayma yapar (+ işleci, sayısal türlere uygulandığında numaraları ekler gibi), ancak karmaşık bir türe uygulandığında, ne yaparsa yapsın, istediğini yapabilir. bu tip için anlam.
gbjbaanb

1
* aynı zamanda başvuru giderme için kullanılır (akıllı işaretçiler ve yineleyiciler tarafından yapıldığı gibi); iyi ve kötü aşırı yükleme arasındaki sınırı nereye koyacağı belli değil
martinkunev

Sadece hiçbir yerde olmazdı, j'nin tanımında olurdu.
Andy

19

IMHO, operatörün aşırı yüklenmesi gibi dil özellikleri programlayıcıya daha fazla güç sağlar. Ve hepimizin bildiği gibi, büyük bir güçle büyük sorumluluk gelir. Size daha fazla güç veren özellikler aynı zamanda kendinizi ayağınızdan vurmanın daha fazla yolunu sunar ve açık bir şekilde, akıllıca kullanılmalıdır.

Örneğin, mükemmel aşırı mantıklı +ya *yönelik operatörü class Matrixveya class Complex. Herkes anında ne anlama geldiğini anlayacaktır. Öte yandan, bana göre, +dize birleştirme anlamına geldiği gerçeği, Java bunu dilin bir parçası olarak yapsa da ve STL std::stringoperatör aşırı yüklemesini kullanmak için yapsa bile açık değildir .

Operatör aşırı yüklenmesinin kodu daha açık hale getirmesinin bir başka iyi örneği, C ++ 'daki akıllı işaretçilerdir. Akıllı işaretçilerin olabildiğince normal işaretçiler gibi davranmasını istiyorsunuz, bu nedenle üniteyi *ve ->operatörleri aşırı yüklemek mükemmel bir fikirdir .

Temelde, operatörün aşırı yüklenmesi, bir fonksiyonu adlandırmanın başka bir yolundan başka bir şey değildir. Ve fonksiyonların isimlendirilmesinde bir kural vardır: ismin tanımlayıcı olması gerekir, bu da fonksiyonun ne yaptığını hemen belli eder. Aynı kesin kural operatör aşırı yüklemesi için de geçerlidir.


1
Son iki cümleniz, operatörün aşırı yüklenmesine itirazın özünü alır: tüm kodların derhal belli olması arzusu.
Larry Coleman,

2
M * N'in ne anlama geldiği, M ve N'nin Matrix tipi olduğu açık değil mi?
Dima,

2
@Fred: Hayır. Bir çeşit matris çarpımı var. Bir mxn matrisini bir nxk matrisiyle çarpabilir ve bir mxk matrisini alabilirsiniz.
Dima,

1
@FredOverflow: Üç boyutlu bir vektörü çarpmanın farklı yolları vardır, biri size bir skaler verir, diğeri size başka bir üç boyutlu vektör verir ve bu nedenle *bunlar için aşırı yüklenme karışıklığa neden olabilir. Muhtemelen operator*()nokta ürün operator%()için ve çapraz ürün için kullanabilirsiniz, ancak bunu genel kullanımlı bir kütüphane için yapmazdım.
David Thornley

2
Beckett @Martin: Hayır C ++ yeniden düzenlemek için izin verilmez A-Bolarak B-Aya ve tüm operatörler bu desen izleyin. Her zaman bir istisna olmasına rağmen: derleyici bunun önemli olduğunu kanıtlayabildiğinde , her şeyi yeniden düzenlemesine izin verilir.
Sjoerd

9

Haskell'de "+", "-", "*", "/" etc sadece (infix) fonksiyonlardır.

Bir ek işlevine "artı", "4 artı 2" deki gibi bir ad vermelisiniz? Neden olmasın, eğer ekleme işleviniz ne ise. "Artı" işlevinizi "+" olarak mı adlandırmalısınız? Neden olmasın.

Bence "operatörler" ile ilgili meselenin, çoğunlukla matematiksel işlemlere benzedikleri ve bunları yorumlamanın pek fazla yolu olmadığı ve bu nedenle böyle bir yöntemin / işlevin / işlevin ne yaptığı konusunda yüksek beklentiler olduğunu düşünüyorum.

EDIT: daha net anlaştım


Erm, C'den miras kalanlar dışında, C ++ (ve Fred'in sorduğu şey) hemen hemen aynı şeyi yapıyor. Şimdi bunun iyi ya da kötü olup olmadığına ne dersin?
sbi

@sbi Ben Aslında bile C aşırı yüklenmiş operatörleri ... Sen bunları kullanabilirsiniz ... operatör aşırı yüklenmesini seviyorum int, float, long longve ne olursa olsun. Peki bu ne hakkında?
FUZxxl

@ FUZxxl: Bu tamamen yerleşik olanları aşırı kullanan kullanıcı tanımlı operatörler ile ilgilidir.
sbi

1
@sbi Haskell yerleşik ve kullanıcı tanımlı arasında bir ayrım yoktur . Tüm operatörler eşittir. Önceden tanımlanmış tüm öğeleri silen ve bazı operatörler de dahil olmak üzere sıfırdan bir şey yazmanıza izin veren bazı uzantıları açabilirsiniz.
FUZxxl

@ FUZxxl: Bu iyi olabilir, fakat karşı aşırı yüklenmiş operatörler genellikle +farklı yerleşik sayı türleri için yerleşik kullanım yerine, kullanıcı tarafından tanımlanan aşırı yüklemeler oluşturmaya karşı çıkıyorlar . bu yüzden benim yorumum.
sbi

7

Gördüğüm diğer cevaplara dayanarak, sadece operatörün aşırı yüklenmesine asıl itirazın hemen bariz kod için bir istek olduğu sonucuna varabilirim.

Bu iki nedenden dolayı trajik:

  1. Mantıklı bir sonuca varılan kodun derhal açıkça anlaşılması gerektiği prensibi hepimizi hala COBOL'de kodlardı.
  2. Hemen açık olan koddan öğrenemezsin. Nasıl çalıştığını düşünmek için biraz zaman harcadığınızda mantıklı olan koddan öğrenirsiniz.

Yine de koddan öğrenmek her zaman ana amaç değildir. "X özelliği arızalı, bunu yazan kişi şirketten ayrıldı ve en kısa zamanda düzeltmeniz gerekiyor" gibi bir durumda, hemen hemen açık olan bir kodum var.
Hatasatz

5

Biraz katılıyorum.

Eğer yazarsanız multiply(j,5), jskaler veya matris tipinde multiply()olabilir, ne jolduğuna bağlı olarak daha fazla veya daha az karmaşık hale getirebilir . Bununla birlikte, aşırı yükleme fikrini tamamen bırakırsanız, fonksiyonun isimlendirilmesi multiply_scalar()veya multiply_matrix()altında ne olduğunu açıklığa kavuşturmak gerekir.

Birçoğumuzun bunu bir şekilde tercih edeceği bir kod ve çoğumuzun diğerini tercih edeceği bir kod var. Bununla birlikte, kodun çoğu, bu iki uç nokta arasındaki orta zemine düşüyor. Tercih ettiğiniz şey, geçmişinize ve kişisel tercihlerinize bağlıdır.


İyi bir nokta. Bununla birlikte, aşırı yüklemeyi tamamen bırakmak genel programlama ile hoş
görünmez

@ FredO: Tabii ki değil. Ancak, genel programlama tamamen farklı algoritmalar için aynı algoritmayı kullanmakla ilgilidir, bu nedenle tercih edenler multiply_matrix()de genel programlamayı sevmezler.
sbi

2
İsimler konusunda iyimsersin, değil mi? Çalıştığım bazı yerlerden yola çıkarak 'multiply () `ve' multiplym ()` gibi ya da belki real_multiply()ya da böyle isimler bekliyordum . Geliştiriciler genellikle isimlerle iyi değillerdir ve operator*()en azından tutarlı olacaklardır.
David Thornley

@David: Evet, isimlerin kötü olabileceği gerçeğini atladım. Fakat o zaman operator*()aptalca jbir şey yapabileceğini, beş işlev çağrısı içeren bir ifadeyi değerlendiren bir makro olduğunu varsayabiliriz . O zaman artık iki yaklaşımı karşılaştıramazsınız. Ancak, evet, her şeyi iyi bir şekilde adlandırmak, ne kadar zaman alırsa iyi olsa da zordur.
sbi

5
@David: İşlerin isimlendirilmesi zor olduğu için isimler programlama dillerinden çıkarılmalı, değil mi? Onları yanlış anlamak çok kolay! ;-)
fredoverflow

4

Operatör aşırı yüklenmesinde iki sorun görüyorum.

  1. Aşırı yükleme, programlayıcı tarafından tasarlanmamış olsa bile operatörün anlamını değiştirir. Örneğin, aşırı zaman &&, ||ya ,, bu operatörlerin yerleşik varyantları ima edilmektedir dizi noktaları (yanı sıra mantıksal operatörlerin kısa devre davranışı) kaybederler. Bu nedenle, dil izin verdiği halde bu operatörleri aşırı yüklememek daha iyidir.
  2. Bazı insanlar operatörün aşırı yüklenmesini bu kadar hoş bir özellik olarak görüyorlar, uygun bir çözüm olmasa bile her yerde kullanmaya başlıyorlar. Bu, diğer kişilerin aşırı yönde tepki vermesine ve operatörün aşırı yüklenmesine karşı uyarılmasına neden olur. Her iki gruba katılmıyorum, ancak orta yol al: Operatör aşırı yüklemesi az miktarda ve sadece ne zaman kullanılmalı
    • Aşırı yüklenmiş operatör, hem alan uzmanları hem de yazılım uzmanları için doğal bir anlama sahiptir. Bu iki grup operatör için doğal anlam üzerinde anlaşamıyorsa, aşırı yüklemeyin.
    • İlgili tür (ler) için operatör için doğal bir anlam yoktur ve derhal bağlam (tercihen aynı ifade, ancak birkaç satırdan fazla değil) her zaman operatörün anlamının ne olduğunu açıkça belirtir. Bu kategorinin bir örneği operator<<akışlar için olabilir.

1
Benden +1, ancak ikinci argüman aynı şekilde mirasa da uygulanabilir. Birçok insan miras hakkında bir ipucuna sahip değildir ve bunu her şeye uygulamaya çalışmaktadır. Bence çoğu programcı kalıtımın kötüye kullanılması konusunda hemfikirdir. Bu, mirasın “kötü” olduğu ve programlama dillerinden vazgeçilmesi gerektiği anlamına mı geliyor? Yoksa yararlı olabileceğinden onu bırakmalı mıyız?
fredoverflow

@FredOverflow İkinci argüman "yeni ve sıcak" olan her şeye uygulanabilir. Operatörün aşırı yüklenmesini bir dilden kaldırmak için bir tartışma olarak değil, insanların neden buna karşı savunduklarının bir nedeni olarak vermiyorum. Endişe duyduğum kadarıyla, operatörün aşırı yüklenmesi yararlıdır ancak dikkatli kullanılmalıdır.
Bart van Ingen Schenau,

IMHO, dizilemeyi gerektirmeyen &&ve aşırı ||şekilde aşırı yüklenmeye izin vermek büyük bir hataydı (IMHO, eğer C ++ aşırı yüklenmeye izin verecekse, ilk işlev gerekli olacak şekilde özel bir "iki işlevli" biçim kullanmalıydı. bir tamsayıya dolaylı olarak dönüştürülebilen bir tip döndürmek için, ikinci fonksiyon iki ya da üç argüman alabilir, ikinci fonksiyonun "ekstra" argümanı birinci fonksiyonun geri dönüş tipidir. eğer sıfırdan
dönmezse

Elbette, bu virgül operatörünün aşırı yüklenmesine izin vermek kadar tuhaf değil. Bu arada, gerçekten görmediğim, ama yapmak istediğim tek bir aşırı yükleme olayı, bir üyeyi açığa vurmak yerine, kendi sınıfında foo.bar[3].Xele alınacak gibi bir ifadeye izin veren, sıkı bağlayıcı bir üye erişimi aracı olacaktır. aboneliği destekleyebilir ve daha sonra bir üyeyi ortaya çıkarabilir . Biri gerçek üye erişimi yoluyla değerlendirmeyi zorlamak isterse, biri yazacaktır . foofooX((foo.bar)[3]).X
supercat

3

Kişisel deneyimime dayanarak, birden fazla yönteme izin veren, ancak operatöre aşırı yükleme yapmayan Java yolu, bir operatör gördüğünüzde ne yaptığını tam olarak bildiğiniz anlamına gelir .

*Garip kod çağırıp çalıştırmadığını görmek zorunda değilsiniz ancak bunun çarpma olduğunu ve Java Dili Şartnamesi tarafından tanımlandığı şekilde davrandığını bilmek zorunda değilsiniz . Bu, programcı tarafından tanımlanan tüm küçük eşya öğelerini bulmak yerine gerçek davranışa konsantre olabileceğiniz anlamına gelir.

Başka bir deyişle, operatörün aşırı yüklenmesini yasaklamak , yazara değil okuyucunun yararınadır ve programların bakımını kolaylaştırır!


+1, bir ihtarla: C ++, kendini asman için yeterli ipi verir. Fakat eğer C ++ 'ta bağlantılı bir liste uygulamak istersem, nth elemanına erişmek için [] kullanabilmek istiyorum. İşleçleri (matematiksel olarak konuşma) geçerli oldukları veriler için kullanmak mantıklıdır.
Michael K,

@Michael, sentaksla yaşayamazsın list.get(n)?

@ Thorbjørn: Gerçekten de iyidir, belki de kötü bir örnek. Zaman daha iyi olabilir - aşırı yükleme +, - zamandan ziyade mantıklı olur (bir başka Zaman).
Michael K

4
@Michael: Bağlantılı listeler hakkında, std::listaşırı yüklenmez operator[](veya listeye başka bir indeksleme aracı vermez), çünkü böyle bir işlem O (n) olacaktır ve verim ile ilgileniyorsanız, bir liste arayüzü böyle bir işlevi göstermemelidir. İstemciler, dizinleri olan bağlantılı listeler üzerinde yinelemeye çalışarak O (n) algoritmalarını gereksiz yere O (n ^ 2) yapar. Java kodunda oldukça sık görüyorsunuz, özellikle insanlar Listkarmaşıklığı tamamen ortadan kaldırmayı amaçlayan arayüzle çalışıyorlarsa .
fredoverflow

5
@Thor: "Ama emin olmak için :) 'ı kontrol etmelisin ..." Yine, bu operatör aşırı yüklenmesine bağlı değildir . Görüyorsanız time.add(anotherTime), aynı zamanda kütüphane programcısının "doğru" ekleme işlemini uygulayıp uygulamadığını kontrol etmeniz gerekecektir (ne demekse).
fredoverflow

3

Aşırı yükleme a * bve arama arasındaki bir fark multiply(a,b), ikincisinin kolayca engellenebilmesidir. Eğer multiplyfonksiyon farklı türleri için aşırı yüklenmesini o zaman işlev türleri itibarıyla izlemek zorunda kalmadan, yapacak tam olarak ne öğrenebilirim ave b.

Linus Torvalds'ın, operatörün aşırı yüklenmesi hakkında ilginç bir argümanı var. Değişikliklerin çoğunun e-posta yoluyla yamalar yoluyla gönderildiği linux çekirdeği geliştirme gibi bir durumda, bakım sağlayıcıların bir düzeltme ekinin her değişiklik etrafında yalnızca birkaç içerik satırıyla neler yapabileceğini anlamaları önemlidir. Eğer fonksiyonlar ve operatörler aşırı yüklenmediyse, yama, tüm tiplerin ne olduğunu hesaplayarak değiştirilmiş dosyadan geçmek zorunda olmadığınız ve aşırı yüklenmiş operatörleri kontrol etmek zorunda olmadığınızdan, bağlamdan bağımsız bir şekilde daha kolay okunabilir.


Linux çekirdeği saf C de gelişmemiş mi? Neden bu bağlamda (operatör) aşırı yüklenmesini tartışıyorsunuz?
fredoverflow

Endişeleri, dile bakılmaksızın, benzer bir gelişim sürecine sahip herhangi bir proje için aynıdır. Aşırı aşırı yükleme, devam etmeniz gereken tek şey bir yama dosyasındaki birkaç satırsa, değişikliklerin etkisini anlamayı zorlaştırabilir.
Scott Wales

@ FredOverflow: Linux çekirdeği gerçekten GCC C'de. Bazı durumlarda C'ye neredeyse C ++ hissi veren her türlü uzantıyı kullanır. Bazı süslü tip manipülasyonları düşünüyorum.
Zan Lynx

2
@Scott: C de programlanan projelere göre aşırı yüklenmenin “kötülüğünü” tartışmanın bir anlamı yoktur, çünkü C, işlevleri aşırı yükleme yeteneğine sahip değildir.
fredoverflow

3
Linus Torvalds bana dar bir bakış açısına sahip görünüyor. Bazen Linux çekirdek programlaması için gerçekten kullanışlı olmayan şeyleri, onları genel kullanım için uygun hale getirmemiş gibi eleştirir. Yıkılma bir örnektir. Güzel bir VCS, ancak Linux çekirdeği geliştirme işleminin gerçekten dağınık bir VCS'ye ihtiyacı var, bu nedenle Linus genel olarak SVN'yi eleştirdi.
David Thornley

2

Beklentilerin kırılmasıyla ilgili bir şey olduğundan şüpheleniyorum. Ben C ++ 'ya alışkınsınız, operatör davranışına tamamen dil tarafından dikte edilmemişsiniz ve bir operatör tuhaf bir şey yaptığında şaşırmayacaksınız. Bu özelliğe sahip olmayan dillere alışkınsanız ve ardından C ++ kodunu görüyorsanız, diğer dillerden beklentileri de beraberinde getirir ve aşırı yüklenmiş bir operatörün korkak bir şey yaptığını keşfettiğinizde büyük bir şaşkınlık yaşayabilirsiniz.

Şahsen ben bir fark olduğunu düşünüyorum. Dilin yerleşik sözdiziminin davranışını değiştirebileceğiniz zaman, nedene göre daha opak hale gelir. Meta programlamaya izin vermeyen diller sözdizimsel olarak daha az güçlüdür, ancak kavramsal olarak anlaşılması daha kolaydır.


Aşırı yüklenmiş operatörler asla "garip bir şey" yapmamalıdır. Elbette karmaşık bir şey yaparsa sorun değil. Ancak, yalnızca biri açık bir anlamı olduğunda aşırı yüklenir.
Sjoerd

2

C ++ 'a aşırı yüklenme matematik operatörlerinin asıl mesele olmadığını düşünüyorum. Aşırı yükleme işleçlerinin ifadenin içeriğine (örneğin türüne) dayanmaması gerektiğini düşünüyorum "kötülük". Örneğin aşırı yükleme , [ ] ( ) -> ->* new deleteve hatta düzensizlik *. Asla değişmemesi gereken bu operatörlerden belirli bir beklenti kümeniz var.


+1 [] ile ++ eşdeğerini yapmayın.
Michael K,

3
Eğer biz Bahsettiğiniz operatörleri aşırı mümkün olmamalıdır söylüyorsunuz hiç ? Yoksa sadece aklı başında tutmaları için aşırı yüklememiz gerektiğini mi söylüyorsun? Çünkü kaplar olmadan operator[], functers olmadan operator(), akıllı işaretçiler olmadan operator->ve benzeri şeyler görmekten nefret ederim .
fredoverflow

Diyelim ki, operatörün matematik işlemleriyle aşırı yüklenme problemi, bu operatörlere göre daha küçük. Matematik operatörleri ile akıllıca veya çılgınca bir şey yapmak zahmetli olabilir, ancak listelediğim operatörler, insanların genellikle operatör olarak düşünmek yerine, temel dil unsurları olarak her zaman dilin tanımladığı beklentiyi karşılaması gerekir . []her zaman dizi benzeri bir erişimci ->olmalı ve her zaman bir üyeye erişmek anlamına gelmelidir. Aslında bir dizi veya farklı bir kapsayıcı olması ya da akıllı bir işaretçi olup olmaması önemli değildir.
Allon Guralnek

2

Anladığım kadarıyla Joel'in saklanma konusundaki tartışmasını beğenmedin. Ben de değil. Yerleşik sayısal türler gibi şeyler veya 'matris gibi kendi olanlar için' + 'kullanmak gerçekten çok daha iyidir. Bunun, iki matrisi '.multiply ()' yerine '*' ile çarpabilmek için düzenli ve zarif olduğunu kabul ediyorum. Ve sonuçta her iki durumda da aynı tür soyutlamaları elde ettik.

Burada acı veren, kodunuzun okunabilirliğidir. Gerçek hayattaki durumlarda, matris çarpımının akademik örneğinde değil. Özellikle, eğer diliniz başlangıçta dil merkezinde bulunmayan operatörleri tanımlamaya izin veriyorsa, örneğin =:=. Bu noktada bir sürü ek soru ortaya çıkıyor. Bu lanet operatör ne hakkında? Yani, o şeyin önceliği nedir? Birliktelik nedir? Gerçekten hangi sırada yapılır a =:= b =:= c?

Bu zaten operatörün aşırı yüklenmesine karşı bir argüman. Hala ikna olmadınız mı? Öncelik kurallarının kontrol edilmesi sizi 10 saniyeden fazla sürmedi. Tamam, daha ileri gidelim.

Operatör aşırı yüklemesine izin veren bir dil kullanmaya başlarsanız, örneğin adı 'S' ile başlayan popüler olanı, kütüphane tasarımcılarının operatörleri geçersiz kılmayı sevdiğini hızlıca öğreneceksiniz. Tabii ki iyi eğitilmişler, en iyi uygulamaları takip ediyorlar (burada sinizm yok) ve tüm API'leri ayrı ayrı baktığımızda mükemmel bir anlam ifade ediyor.

Şimdi tek bir kod parçasında birlikte aşırı yükleme yapan operatörlerin yoğun şekilde kullanılmasını sağlayan birkaç API kullanmanız gerektiğini hayal edin. Ya da daha iyisi - böyle eski bir kodu okumak zorundasın. Bu, operatör aşırı yüklemesi gerçekten çok kötü olduğunda. Temel olarak, bir yerde çok fazla aşırı yüklenmiş operatör olması durumunda, program kodunuzda diğer alfa sayısal olmayan karakterlerle karışmaya başlarlar. Gerçekten operatör olmayan alfa sayısal olmayan karakterlerle karışacaklar, daha ziyade bloklar ve kapsamlar gibi şeyleri tanımlayan, akış kontrol deyimlerini şekillendiren veya bazı meta olaylarını ifade eden bazı temel dil gramer unsurları ile karışacaklar. Bu görsel karışıklığı anlamak için gözlükleri koymanız ve gözlerinizi LCD ekrana 10 cm yaklaştırmanız gerekir.


1
Mevcut operatörlere aşırı yükleme yapmak ve yeni operatörler icat etmek aynı şey değil, benden +1.
fredoverflow

1

Genel olarak, operatör aşırı yüklemesini sezgisel olmayan şekillerde kullanmaktan kaçınırım. Diğer bir deyişle, sayısal bir sınıfım varsa, aşırı yükleme * kabul edilebilir (ve teşvik edilir). Bununla birlikte, eğer bir Sınıf Çalışanı varsa, aşırı yükleme ne yapardı? Başka bir deyişle, operatörlere okumayı ve anlamayı kolaylaştıran sezgisel yollarla aşırı yükleme.

Kabul edilebilir / cesaret:

class Complex
{
public:
    double r;
    double i;

    Complex operator*(const Compex& rhs)
    {
        Complex result;
        result.r = (r * rhs.r) - (i * rhs.i);
        result.i = (r * rhs.i) + (i * rhs.r);
        return result;
    }
};

Kabul edilemez:

class Employee
{
public:
    std::string name;
    std::string address;
    std::string phone_number;

    Employee operator* (const Employee& e)
    {
        // what the hell do I do here??
    }
};

1
Çalışanların çarpılması? Kuşkusuz, bu çuvallanabilir bir suçtur, eğer toplantı masasında yaparlarsa, yani.
gbjbaanb

1

Burada daha önce söylenenlere ek olarak, operatörün aşırı yüklenmesine karşı bir tartışma daha var. Gerçekten, yazarsanız +, bir şeye bir şey eklemek istediğiniz anlamına geldiği açıktır. Ancak bu her zaman böyle değildir.

C ++, böyle bir durumun harika bir örneğini sunar. Nasıl stream << 1okunması gerekiyor? dere 1 sola kaydırıldı? Açıkça C ++ 'da <<' nın aynı zamanda akıma da yazdığını bilmediğiniz sürece belli değil. Ancak, bu işlem bir yöntem olarak gerçekleştirilirse, akıllı bir geliştirici yazmaz o.leftShift(1), böyle bir şey olur o.write(1).

Sonuç olarak, operatörün aşırı yüklenmesini kullanılamaz duruma getirerek, dil programcıların işlemlerin isimleri hakkında düşünmesini sağlar. Seçilen isim mükemmel olmasa bile, bir ismi işaretten daha yanlış anlamak yine de daha zordur.


1

Açıklanan yöntemlerle karşılaştırıldığında, operatörler daha kısadır, ancak parantez gerektirmezler. Parantezler yazmak için uygun değildir. Ve onları dengelemelisin. Toplamda, herhangi bir yöntem çağrısı bir operatöre kıyasla üç karakter düz gürültü gerektirir. Bu, operatörleri kullanmayı çok cazip kılar.
Neden başkasının bu isteyeyim: cout << "Hello world"?

Aşırı yükleme ile ilgili sorun, çoğu programcının inanılmaz derecede tembel olduğu ve programcıların çoğunun göze alamayacağıdır.

C ++ programcılarını operatör aşırı yüklenmesinin kötüye kullanımına iten şey onun varlığı değil, yöntem çağrısı yapmak için daha temiz bir yolun olmamasıdır. Ve insanlar sadece operatörün aşırı yüklenmesinden korkmuyorlar çünkü mümkün, ama bittiği için.
Örneğin, Ruby ve Scala'da kimsenin operatörün aşırı yüklenmesinden korkmadığını unutmayın. Operatörlerin kullanımının yöntemlerden çok daha kısa olmasının dışında, bir başka neden de, Ruby'nin operatörün aşırı yüklenmesini makul bir asgariye indirgemesini sağlarken, Scala kendi operatörlerinizi açıklamanızı ve böylece çarpışmadan kaçınmanızı sağlar.


veya, bir olayı bir temsilciye bağlamak için + = kullanmak için C # 'da. Programcının aptallığı için dil özelliklerini suçlamanın yapıcı bir yol olduğunu sanmıyorum.
gbjbaanb

0

Operatör Aşırı Yüklemesinin korkutucu olmasının nedeni, THINK'yi hiçbir zaman *basitçe "çarpma" anlamına gelmeyecek kadar fazla sayıda programcının olmamasıdır; oysa, foo.multiply(bar)en azından anında bu programcıya, birinin özel çarpma yöntemi yazdığını işaret eder. . Bu noktada nedenini merak eder ve araştırmaya giderlerdi.

2 karşılaştırmayı alacak ve "birbirinden diğerine değerleri uygulayacak ve bir boolean döndürecek" CreateValues ​​"olarak adlandırılan yöntemler yaratacak üst düzey pozisyonlarda olan" iyi programcılar "ile çalıştım. Veya "LoadTheValues" adlı bir metoda, diğer 3 nesne için veri tabanına gider, değerler alır, hesaplamalar yapar, değiştirir thisve veritabanına kaydeder .

Bu tür programcıların bulunduğu bir ekip üzerinde çalışıyorsam, üzerinde çalıştıkları şeyleri araştırmayı hemen biliyorum. Eğer bir operatörü aşırı yükledilerse, yaptıklarını varsaymak ve aramaya gitmek dışında yaptıklarını bilmenin hiçbir yolu yoktur.

Mükemmel bir dünyada veya mükemmel programcılara sahip bir ekipte, operatörün aşırı yüklenmesi muhtemelen harika bir araçtır. Yine de mükemmel bir programcı ekibi üzerinde çalışmam gerekiyor, bu yüzden korkutucu.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.