Saya baru saja membaca salah satu artikel Joel di mana dia berkata:
Secara umum, saya harus mengakui bahwa saya sedikit takut dengan fitur bahasa yang menyembunyikan banyak hal . Ketika Anda melihat kode
i = j * 5;
... di C Anda tahu, setidaknya, bahwa j dikalikan lima dan hasilnya disimpan di i.
Tetapi jika Anda melihat potongan kode yang sama di C ++, Anda tidak tahu apa-apa. Tidak ada. Satu-satunya cara untuk mengetahui apa yang sebenarnya terjadi di C ++ adalah untuk mengetahui apa tipe i dan j, sesuatu yang mungkin dinyatakan di tempat lain. Itu karena saya mungkin tipe yang
operator*
kelebihan beban dan melakukan sesuatu yang sangat lucu ketika Anda mencoba melipatgandakannya.
(Menekankan milikku.) Takut fitur bahasa yang menyembunyikan sesuatu? Bagaimana Anda bisa takut akan hal itu? Bukankah menyembunyikan sesuatu (juga dikenal sebagai abstraksi ) adalah salah satu ide kunci pemrograman berorientasi objek? Setiap kali Anda memanggil metode a.foo(b)
, Anda tidak tahu apa yang mungkin dilakukan. Anda harus mencari tahu jenis a
dan b
apa, sesuatu yang mungkin dinyatakan di tempat lain. Jadi haruskah kita menyingkirkan pemrograman berorientasi objek, karena menyembunyikan terlalu banyak hal dari programmer?
Dan j * 5
apa bedanya dengan j.multiply(5)
yang Anda mungkin harus menulis dalam bahasa yang tidak mendukung kelebihan operator? Sekali lagi, Anda harus mencari tahu jenis j
dan mengintip ke dalam multiply
metode, karena lihatlah, j
mungkin dari jenis yang memiliki multiply
metode yang melakukan sesuatu yang sangat cerdas.
"Muahaha, aku seorang programmer jahat yang menyebutkan metode multiply
, tetapi apa yang sebenarnya dilakukannya benar-benar tidak jelas dan tidak intuitif dan sama sekali tidak ada hubungannya dengan mengalikan hal-hal." Apakah itu skenario yang harus kita pertimbangkan ketika merancang bahasa pemrograman? Maka kita harus meninggalkan pengidentifikasi dari bahasa pemrograman dengan alasan bahwa mereka mungkin menyesatkan!
Jika Anda ingin tahu apa yang dilakukan metode, Anda dapat melihat dokumentasi atau mengintip ke dalam implementasi. Kelebihan operator hanya gula sintaksis, dan saya tidak melihat bagaimana itu mengubah permainan sama sekali.
Tolong beri tahu saya.
Jawaban:
Abstraksi 'menyembunyikan' kode sehingga Anda tidak perlu khawatir tentang cara kerja bagian dalam dan sering kali Anda tidak dapat mengubahnya, tetapi tujuannya bukan untuk mencegah Anda melihatnya. Kami hanya membuat asumsi tentang operator dan seperti kata Joel, itu bisa di mana saja. Memiliki fitur pemrograman yang mengharuskan semua operator kelebihan beban untuk didirikan di lokasi tertentu dapat membantu menemukannya, tetapi saya tidak yakin itu membuatnya lebih mudah menggunakannya.
Saya tidak melihat membuat * melakukan sesuatu yang tidak menyerupai perkalian lebih baik daripada fungsi yang disebut Get_Some_Data yang menghapus data.
sumber
<<
operator yang didefinisikan pada stream yang tidak ada hubungannya dengan pergeseran bitwise, tepat di perpustakaan standar C ++.IMHO, fitur bahasa seperti overloading operator memberi programmer lebih banyak kekuatan. Dan, seperti kita semua tahu, dengan kekuatan besar datang tanggung jawab besar. Fitur yang memberi Anda lebih banyak kekuatan juga memberi Anda lebih banyak cara untuk menembak diri sendiri di kaki, dan, jelas, harus digunakan dengan bijaksana.
Sebagai contoh, masuk akal untuk membebani
+
atau*
operator yang untukclass Matrix
atauclass Complex
. Semua orang akan langsung tahu artinya. Di sisi lain, bagi saya fakta yang+
berarti rangkaian string sama sekali tidak jelas, meskipun Java melakukan ini sebagai bagian dari bahasa, dan STL tidak untukstd::string
menggunakan overloading operator.Contoh lain yang baik ketika operator overloading membuat kode lebih jelas adalah smart pointer di C ++. Anda ingin pointer cerdas berperilaku seperti pointer biasa sebanyak mungkin, sehingga masuk akal untuk membebani unary
*
dan->
operator.Intinya, overloading operator tidak lebih dari sekadar cara lain untuk memberi nama suatu fungsi. Dan ada aturan untuk fungsi penamaan: nama harus deskriptif, membuatnya segera jelas apa fungsinya. Aturan persis yang sama berlaku untuk overloading operator.
sumber
*
bagi yang lain dapat menyebabkan kebingungan. Bisa dibilang Anda bisa menggunakanoperator*()
untuk produk titik dan produkoperator%()
silang, tapi saya tidak akan melakukannya untuk perpustakaan penggunaan umum.A-B
sebagaiB-A
baik, dan semua operator mengikuti pola itu. Meskipun selalu ada satu pengecualian: ketika kompiler dapat membuktikan itu tidak masalah, itu diperbolehkan untuk mengatur ulang semuanya.Di Haskell "+", "-", "*", "/" dll hanyalah fungsi (infix).
Haruskah Anda menyebutkan fungsi infix "plus" seperti pada "4 plus 2"? Mengapa tidak, jika penambahan adalah fungsi Anda. Haruskah Anda memberi nama "plus" fungsi "+"? Kenapa tidak.
Saya pikir masalah dengan apa yang disebut "operator" adalah, bahwa mereka kebanyakan menyerupai operasi matematika dan tidak ada banyak cara untuk menafsirkannya dan dengan demikian ada harapan yang tinggi tentang apa yang dilakukan oleh metode / fungsi / operator tersebut.
EDIT: membuat poin saya lebih jelas
sumber
int
,float
,long long
dan apa pun. Jadi, tentang apa semua itu?+
untuk tipe angka built-in yang berbeda, tetapi membuat overload yang ditentukan pengguna. karenanya komentar saya.Berdasarkan jawaban lain yang saya lihat, saya hanya bisa menyimpulkan bahwa keberatan sebenarnya terhadap operator kelebihan adalah keinginan untuk kode yang segera jelas.
Ini tragis karena dua alasan:
sumber
Saya agak setuju.
Jika Anda menulis
multiply(j,5)
,j
bisa dari skalar atau tipe matriks, membuatmultiply()
lebih atau kurang kompleks, tergantung pada apaj
. Namun, jika Anda meninggalkan ide kelebihan beban sama sekali, maka fungsi harus dinamaimultiply_scalar()
ataumultiply_matrix()
yang akan membuatnya jelas apa yang terjadi di bawahnya.Ada kode di mana banyak dari kita lebih suka satu arah dan ada kode di mana kebanyakan dari kita lebih suka dengan cara lain. Sebagian besar kode, bagaimanapun, jatuh ke jalan tengah antara dua ekstrem. Apa yang Anda sukai di sana tergantung pada latar belakang dan preferensi pribadi Anda.
sumber
multiply_matrix()
tidak akan menyukai pemrograman generik juga.real_multiply()
atau lebih. Pengembang sering tidak baik dengan nama, danoperator*()
setidaknya akan konsisten.operator*()
mungkin melakukan sesuatu yang bodoh,j
adalah makro mengevaluasi ekspresi yang melibatkan lima panggilan fungsi, dan yang lainnya. Anda kemudian tidak dapat membandingkan kedua pendekatan itu lagi. Tapi, ya, menamai semuanya itu sulit, meskipun sepadan dengan waktu yang dibutuhkan.Saya melihat dua masalah dengan kelebihan operator.
&&
,||
atau,
, Anda kehilangan titik urutan yang tersirat oleh varian bawaan dari operator ini (serta perilaku hubungan pendek dari operator logis). Untuk alasan ini, lebih baik untuk tidak membebani operator ini, bahkan jika bahasa memungkinkan.operator<<
untuk stream.sumber
&&
dan||
dengan cara yang tidak menyiratkan pengurutan adalah kesalahan besar (IMHO, jika C ++ akan memungkinkan kelebihan itu, seharusnya menggunakan format "dua-fungsi" khusus, dengan fungsi pertama diperlukan untuk mengembalikan jenis yang secara implisit dapat dikonversi menjadi bilangan bulat, fungsi kedua dapat mengambil dua atau tiga argumen, dengan argumen "ekstra" dari fungsi kedua menjadi jenis pengembalian yang pertama. Kompiler akan memanggil fungsi pertama dan kemudian, jika kembali bukan nol, evaluasi operan kedua dan panggil fungsi kedua di atasnya.)foo.bar[3].X
ditangani olehfoo
kelas, daripada mengharuskanfoo
untuk mengekspos anggota yang dapat mendukung subskrip dan kemudian mengekspos anggotaX
. Jika seseorang ingin memaksakan evaluasi melalui akses anggota aktual, ia akan menulis((foo.bar)[3]).X
.Berdasarkan pengalaman pribadi saya, cara Java memungkinkan beberapa metode, tetapi tidak kelebihan operator, berarti bahwa setiap kali Anda melihat operator, Anda tahu persis apa yang dilakukannya.
Anda tidak perlu melihat apakah
*
memanggil kode aneh tetapi tahu bahwa itu adalah multiply, dan berperilaku persis seperti cara yang didefinisikan oleh Spesifikasi Bahasa Jawa. Ini berarti Anda dapat berkonsentrasi pada perilaku aktual alih-alih mencari tahu semua hal gawang yang ditentukan oleh programmer.Dengan kata lain, melarang kelebihan operator adalah manfaat bagi pembaca , bukan penulis , dan karenanya membuat program lebih mudah dikelola!
sumber
list.get(n)
sintaks?std::list
tidak kelebihanoperator[]
(atau memberikan cara pengindeksan lain ke dalam daftar), karena operasi seperti itu akan menjadi O (n), dan antarmuka daftar tidak boleh mengekspos fungsi seperti itu jika Anda peduli efisiensi. Klien mungkin tergoda untuk beralih pada daftar tertaut dengan indeks, membuat algoritma O (n) sia-sia O (n ^ 2). Anda melihat itu cukup sering dalam kode Java, terutama jika orang bekerja denganList
antarmuka yang bertujuan untuk menghilangkan kompleksitas abstrak sepenuhnya.time.add(anotherTime)
, Anda juga harus memeriksa apakah pemrogram perpustakaan menerapkan operasi add "dengan benar" (apa pun artinya).Satu perbedaan antara overloading
a * b
dan meneleponmultiply(a,b)
adalah bahwa yang terakhir dapat dengan mudah dipahami. Jikamultiply
fungsi tersebut tidak kelebihan beban untuk jenis yang berbeda maka Anda dapat mengetahui dengan tepat apa fungsi yang akan dilakukan, tanpa harus melacak jenisa
danb
.Linus Torvalds memiliki argumen yang menarik tentang kelebihan operator. Dalam sesuatu seperti pengembangan kernel linux, di mana sebagian besar perubahan dikirim melalui tambalan melalui email, penting bahwa pengelola dapat memahami apa yang akan dilakukan tambalan dengan hanya beberapa baris konteks di sekitar setiap perubahan. Jika fungsi dan operator tidak kelebihan beban maka tambalan dapat lebih mudah dibaca dalam konteks independen, karena Anda tidak harus melalui file yang diubah untuk mengetahui semua jenis dan memeriksa untuk operator kelebihan beban.
sumber
Saya menduga itu ada hubungannya dengan melanggar harapan. Saya sudah terbiasa dengan C ++, Anda terbiasa dengan perilaku operator yang tidak didikte sepenuhnya oleh bahasa, dan Anda tidak akan terkejut ketika operator melakukan sesuatu yang aneh. Jika Anda terbiasa dengan bahasa yang tidak memiliki fitur itu, dan kemudian melihat kode C ++, Anda membawa harapan dari bahasa-bahasa lain, dan mungkin terkejut ketika Anda menemukan bahwa operator kelebihan beban melakukan sesuatu yang funky.
Secara pribadi saya pikir ada perbedaan. Saat Anda dapat mengubah perilaku sintaksis bawaan bahasa, itu menjadi lebih buram untuk dipikirkan. Bahasa yang tidak memungkinkan pemrograman meta secara sintaksis kurang kuat, tetapi secara konsep lebih mudah dipahami.
sumber
Saya pikir operator matematika yang kelebihan beban bukan masalah sebenarnya dengan operator yang kelebihan muatan di C ++. Saya pikir operator overload yang tidak boleh bergantung pada konteks ekspresi (yaitu tipe) adalah "jahat". Misalnya overloading
,
[ ]
( )
->
->*
new
delete
atau bahkan unary*
. Anda memiliki seperangkat harapan tertentu dari operator yang tidak boleh berubah.sumber
operator[]
, tanpa fungsioperator()
, petunjuk pintar tanpaoperator->
dan sebagainya.[]
harus selalu berupa->
pengakses mirip array, dan harus selalu berarti mengakses anggota. Tidak masalah apakah itu sebenarnya array atau wadah yang berbeda, atau apakah itu pointer cerdas atau tidak.Saya sangat memahami Anda tidak suka argumen Joel tentang persembunyian. Aku juga tidak. Memang jauh lebih baik menggunakan '+' untuk hal-hal seperti tipe numerik bawaan atau untuk tipe Anda sendiri, misalnya, matriks. Saya akui ini rapi dan anggun untuk bisa melipatgandakan dua matriks dengan '*' alih-alih '.multiply ()'. Lagi pula, kami memiliki jenis abstraksi yang sama dalam kedua kasus tersebut.
Apa yang menyakitkan di sini adalah keterbacaan kode Anda. Dalam kasus kehidupan nyata, bukan dalam contoh akademis perkalian matriks. Terutama jika bahasa Anda memungkinkan untuk mendefinisikan operator yang awalnya tidak ada di inti bahasa, misalnya
=:=
. Banyak pertanyaan tambahan muncul pada saat ini. Tentang apa operator itu? Maksud saya apa yang diutamakan dari hal itu? Apa asosiatifnya? Di mana urutan yanga =:= b =:= c
benar - benar dieksekusi?Itu sudah menjadi argumen terhadap overloading operator. Masih belum yakin? Memeriksa aturan diutamakan membawa Anda tidak lebih dari 10 detik? Ok, mari kita melangkah lebih jauh.
Jika Anda mulai menggunakan bahasa yang memungkinkan overloading operator, misalnya bahasa populer yang namanya dimulai dengan 'S', Anda akan dengan cepat mengetahui bahwa perancang perpustakaan suka mengganti operator. Tentu saja mereka berpendidikan tinggi, mereka mengikuti praktik terbaik (tidak ada sinisme di sini) dan semua API mereka masuk akal ketika kita melihatnya secara terpisah.
Sekarang bayangkan Anda harus menggunakan beberapa API yang banyak menggunakan operator kelebihan beban bersama dalam satu kode. Atau bahkan lebih baik - Anda harus membaca beberapa kode lama seperti itu. Inilah saat operator kelebihan muatan benar-benar menyebalkan. Pada dasarnya jika ada banyak operator kelebihan beban di satu tempat mereka akan segera mulai berbaur dengan karakter non-numerik lain dalam kode program Anda. Mereka akan berbaur dengan karakter non-alfanumerik yang bukan benar-benar operator tetapi lebih merupakan beberapa elemen tata bahasa bahasa yang lebih mendasar yang mendefinisikan hal-hal seperti blok dan cakupan, membentuk pernyataan kontrol aliran atau menunjukkan beberapa hal meta. Anda harus meletakkan kacamata dan memindahkan mata Anda 10 cm lebih dekat ke layar LCD untuk memahami kekacauan visual itu.
sumber
Secara umum, saya menghindari penggunaan operator yang berlebihan dengan cara yang tidak intuitif. Yaitu, jika saya memiliki kelas numerik, kelebihan * dapat diterima (dan dianjurkan). Namun, jika saya memiliki Karyawan kelas, apa yang akan dilakukan overloading *? Dengan kata lain, membebani operator dengan cara intuitif yang membuatnya mudah dibaca dan dipahami.
Diterima / Didorong:
Tidak dapat diterima:
sumber
Selain apa yang telah dikatakan di sini, ada satu argumen lagi terhadap kelebihan operator. Memang, jika Anda menulis
+
, ini agak jelas bahwa Anda bermaksud menambahkan sesuatu pada sesuatu. Tapi ini tidak selalu terjadi.C ++ itu sendiri memberikan contoh yang bagus dari kasus seperti itu. Bagaimana
stream << 1
seharusnya dibaca? aliran dialihkan ke kiri sebesar 1? Tidak jelas sama sekali kecuali Anda secara eksplisit tahu bahwa << di C ++ juga menulis ke aliran. Namun, jika operasi ini diimplementasikan sebagai metode, tidak ada pengembang waras yang akan menuliso.leftShift(1)
, itu akan menjadi seperti ituo.write(1)
.Intinya adalah bahwa dengan membuat operator kelebihan tidak tersedia, bahasa membuat pemrogram memikirkan nama-nama operasi. Bahkan jika nama yang dipilih tidak sempurna, masih lebih sulit untuk salah mengartikan nama daripada tanda.
sumber
Dibandingkan dengan metode yang dijabarkan, operator lebih pendek, tetapi mereka juga tidak membutuhkan tanda kurung. Kurung relatif tidak nyaman untuk diketik. Dan Anda harus menyeimbangkannya. Secara total, panggilan metode apa pun membutuhkan tiga karakter derau biasa dibandingkan dengan operator. Ini membuat penggunaan operator sangat, sangat menggoda.
Kenapa lagi orang mau ini
cout << "Hello world"
:?Masalah dengan overloading adalah, bahwa kebanyakan programmer sangat malas dan kebanyakan programmer tidak mampu.
Apa yang mendorong pemrogram C ++ untuk penyalahgunaan operator kelebihan adalah bukan kehadirannya, tetapi tidak adanya cara yang lebih rapi untuk melakukan pemanggilan metode. Dan orang-orang tidak hanya takut kelebihan operator karena itu mungkin, tetapi karena itu dilakukan.
Perhatikan bahwa misalnya di Ruby dan Scala tidak ada yang takut kelebihan operator. Terlepas dari kenyataan, bahwa penggunaan operator tidak benar-benar lebih pendek dari metode, alasan lain adalah, bahwa Ruby membatasi kelebihan muatan operator ke minimum yang masuk akal, sementara Scala memungkinkan Anda untuk mendeklarasikan operator Anda sendiri sehingga menghindari tabrakan sepele.
sumber
Alasan Operator Overloading menakutkan, adalah karena ada sejumlah besar programmer yang tidak akan pernah BERPIKIR itu
*
tidak berarti hanya "mengalikan", sedangkan metode sepertifoo.multiply(bar)
setidaknya secara langsung menunjukkan kepada pemrogram itu bahwa seseorang menulis metode pengali kustom . Pada titik itu mereka akan bertanya-tanya mengapa dan pergi menyelidiki.Saya telah bekerja dengan "programmer yang baik" yang berada di posisi tingkat tinggi yang akan menciptakan metode yang disebut "CompareValues" yang akan mengambil 2 argumen, dan menerapkan nilai dari satu ke yang lain dan mengembalikan boolean. Atau metode yang disebut "LoadTheValues" yang akan pergi ke database untuk 3 objek lain, mendapatkan nilai, melakukan perhitungan, memodifikasi
this
dan menyimpannya ke database.Jika saya bekerja dalam tim dengan tipe-tipe programmer seperti itu, saya langsung tahu untuk menyelidiki hal-hal yang telah mereka kerjakan. Jika mereka membebani operator secara berlebihan, saya sama sekali tidak tahu bahwa mereka melakukannya kecuali berasumsi mereka melakukannya dan mencari.
Dalam dunia yang sempurna, atau tim dengan programmer yang sempurna, kelebihan operator mungkin merupakan alat yang fantastis. Saya belum bekerja pada tim programmer yang sempurna, jadi itu sebabnya itu menakutkan.
sumber