Apa aturan dasar dan idiom untuk kelebihan operator?

Catatan Jawaban diberikan dalam urutan tertentu, tetapi karena banyak pengguna mengurutkan jawaban berdasarkan suara, bukan waktu yang mereka berikan, inilah indeks jawaban sesuai urutan yang paling masuk akal:

<sub> (Catatan. Ini berarti Anda memasukkan Pertanyaan yang Sering Diajukan tentang C ++ stack overflow . Jika Anda ingin mengkritik gagasan memberikan FAQ dalam formulir ini, maka mengeposkan pada meta yang memulai semuanya akan menjadi tempat untuk itu. Jawaban untuk pertanyaan ini dilacak dalam obrolan C ++ , di mana ide FAQ dimulai sejak awal, jadi jawaban Anda kemungkinan besar akan dibaca oleh mereka yang mengemukakan ide ini.)

1936
12 дек. atur sbi 12 des. 2010-12-12 15:44 '10 pada 15:44 2010-12-12 15:44
@ 7 jawaban

Operator umum untuk memuat u>

Sebagian besar pekerjaan operator yang kelebihan beban adalah kode ruang ketel. Ini tidak mengherankan, karena operator hanyalah gula sintaksis, pekerjaan mereka yang sebenarnya dapat dilakukan (dan sering diarahkan) ke fungsi sederhana. Tetapi penting bahwa Anda mendapatkan kode boiler. Jika Anda gagal, kode operator Anda tidak akan dikompilasi, atau kode pengguna tidak akan dikompilasi, atau kode pengguna akan terlihat mengejutkan.

Operator penugasan

Ada banyak yang bisa dikatakan tentang penunjukan. Namun, sebagian besar dari mereka telah dikatakan di “FAQ Pel dan Tukar” yang terkenal , jadi saya akan melewatkan sebagian besar dari ini di sini, hanya mendaftar operator penugasan yang ideal untuk tautan:

 X X::operator=(X rhs) { swap(rhs); return *this; } 

Operator Bitshift (digunakan untuk Stream I / O)

Operator bit-shift << dan >> , meskipun mereka masih digunakan dalam antarmuka perangkat keras untuk fungsi manipulasi bit yang mereka warisi dari C, telah menjadi lebih umum sebagai operator input dan output stream yang kelebihan beban di sebagian besar aplikasi. Untuk instruksi yang berlebihan sebagai operator manipulasi bit, lihat bagian di bawah ini tentang operasi aritmatika biner. Untuk menerapkan format kustom dan parse logic Anda sendiri ketika objek Anda digunakan dengan iostreams, lanjutkan.

Operator aliran, di antara operator yang paling sering diisi u>

 std::ostream operator<<(std::ostream os, const T obj) { // write obj to stream return os; } std::istream operator>>(std::istream is, T obj) { // read obj from stream if(  ) is.setstate(std::ios::failbit); return is; } 

Saat menerapkan operator>> secara manual, pengaturan status utas hanya diperlukan ketika pembacaan itu sendiri dilakukan, tetapi hasilnya tidak sesuai dengan yang diharapkan.

Operator Panggilan Fungsi

Operator fungsi panggilan yang digunakan untuk membuat objek fungsional, juga dikenal sebagai functors, harus didefinisikan sebagai fungsi anggota , sehingga selalu memiliki argumen implisit fungsi anggota ini. Selain itu, dapat kelebihan beban untuk menerima sejumlah argumen tambahan, termasuk nol.

Berikut ini contoh sintaksnya:

 class foo { public: // Overloaded call operator int operator()(const std::string y) { // ... } }; 

Gunakan:

 foo f; int a = f("hello"); 

Di pustaka C ++ standar, objek objek selalu disalin. Oleh karena itu, objek fungsi Anda sendiri harus murah untuk disalin. Jika benar-benar diperlukan untuk objek fungsi untuk menggunakan data yang mahal untuk menyalin, lebih baik untuk menyimpan data ini di tempat lain dan merujuk ke objek fungsi.

Operator pembanding

Operator pembanding infiks biner harus, sesuai dengan aturan praktis, diimplementasikan sebagai fungsi non-anggota 1 . Negasi awalan persatuan ! harus (sesuai dengan aturan yang sama) diimplementasikan sebagai fungsi anggota. (tetapi, sebagai aturan, tidak disarankan untuk membebani secara berlebihan.)

Algoritma perpustakaan standar (misalnya, std::sort() ) dan tipe (misalnya, std::map ) akan selalu mengharapkan operator< . Namun, pengguna tipe Anda berharap bahwa semua operator lain juga akan hadir, jadi jika Anda mendefinisikan operator< , pastikan untuk mengikuti aturan dasar ketiga dari kelebihan operator, dan juga menentukan semua operator perbandingan logis lainnya. Cara kanonik untuk mengimplementasikannya adalah sebagai berikut:

 inline bool operator==(const X lhs, const X rhs){  } inline bool operator!=(const X lhs, const X rhs){return !operator==(lhs,rhs);} inline bool operator< (const X lhs, const X rhs){  } inline bool operator> (const X lhs, const X rhs){return operator< (rhs,lhs);} inline bool operator<=(const X lhs, const X rhs){return !operator> (lhs,rhs);} inline bool operator>=(const X lhs, const X rhs){return !operator< (lhs,rhs);} 

Penting untuk dicatat bahwa hanya dua operator ini yang benar-benar melakukan sesuatu, yang lain hanya mengarahkan argumen mereka ke tidak satupun dari dua operator ini untuk melakukan pekerjaan yang sebenarnya.

Sintaks untuk overloading operator boolean biner yang tersisa ( || , > ) mengikuti aturan operator perbandingan. Namun, sangat tidak mungkin bahwa Anda akan menemukan preseden yang masuk akal untuk 2 ini.

1 Seperti semua aturan praktis lainnya, terkadang ada alasan untuk me> Jika ini masalahnya, jangan lupa bahwa operan kiri dari operator perbandingan biner, yang untuk fungsi anggota adalah *this , juga harus berupa const . Dengan demikian, operator pembanding, yang diimplementasikan sebagai fungsi anggota, harus memiliki tanda tangan ini:

 bool operator<(const X rhs) const {  } 

(Catat const di akhir.)

2 Perlu dicatat bahwa versi embedded || dan > menggunakan semantik label. Meskipun ditentukan oleh pengguna (karena merupakan gula sintaksis untuk panggilan metode), jangan gunakan label semantik. Pengguna akan mengharapkan operator ini untuk memiliki label semantik, dan kode mereka mungkin bergantung padanya. Oleh karena itu, sangat disarankan untuk TIDAK PERNAH mengidentifikasi mereka.

Operator aritmatika

Operator aritmatika unary

Operator kenaikan dan penurunan unary hadir dalam rasa prefix dan postfix. Untuk membedakan satu dari yang lain, varian postfix mengambil argumen opsional dari dummy int. Jika Anda menambah atau mengurangi secara berlebihan, pastikan untuk selalu menggunakan versi awalan dan postfix. Berikut adalah implementasi kenaikan secara kanonik, penurunan tersebut mengikuti aturan yang sama:

 class X { X operator++() { // do actual increment return *this; } X operator++(int) { X tmp(*this); operator++(); return tmp; } }; 

Perhatikan bahwa varian postfix diimplementasikan dalam hal awalan. Perhatikan juga bahwa postfix melakukan copy tambahan. 2

Kelebihan minus dan plus unary tidak terlalu umum dan mungkin sebaiknya dihindari. Jika perlu, mereka mungkin kelebihan beban sebagai fungsi anggota.

2 Juga perhatikan bahwa opsi postfix bekerja lebih banyak dan karenanya kurang efisien untuk digunakan daripada versi awalan. Ini adalah alasan yang bagus, sebagai aturan, ia lebih suka menambahkan awalan dengan menambah postfix. Meskipun kompiler biasanya dapat mengoptimalkan pekerjaan tambahan tambahan untuk tipe built-in, mereka mungkin tidak dapat melakukan hal yang sama untuk tipe kustom (yang mungkin sesuatu yang tidak bersalah terlihat seperti daftar iterator). Setelah Anda terbiasa melakukan i++ , sangat sulit untuk tidak melupakan untuk melakukan ++i bukannya i tidak memiliki tipe bawaan (plus Anda harus mengubah kode ketika mengubah jenis), jadi sebaiknya biasakan untuk selalu menggunakan peningkatan awalan jika postfix jelas tidak diperlukan.

Operator aritmatika biner

Untuk operator aritmatika biner, jangan lupa mematuhi kelebihan ketiga dari operator aturan utama: jika Anda memberikan + , juga memberikan += , jika Anda memberikan - , jangan lewatkan -= , dll. Dikatakan bahwa Andrei Koenig adalah orang pertama yang memperhatikan bahwa agen penugasan yang kompleks dapat digunakan sebagai dasar untuk salinan non-komposit mereka. Yaitu, operator + diimplementasikan dalam bentuk += , - diimplementasikan dalam bentuk -= , dll.

Sesuai dengan aturan praktis kami, + dan satelitnya harus non-anggota, sedangkan perbandingan majemuknya ( += , dll.), Yang mengubah argumen kiri mereka, harus menjadi anggota. Berikut adalah contoh kode untuk += dan + , operator aritmatika biner lainnya harus diimplementasikan dengan cara yang sama:

 class X { X operator+=(const X rhs) { // actual addition of rhs to *this return *this; } }; inline X operator+(X lhs, const X rhs) { lhs += rhs; return lhs; } 

operator+= mengembalikan hasilnya dengan referensi, dan operator+ mengembalikan salinan hasilnya. Tentu saja, mengembalikan tautan biasanya lebih efisien daripada mengembalikan salinan, tetapi dalam kasus operator+ tidak ada cara untuk menyalinnya. Saat Anda menulis a + b , Anda mengharapkan hasilnya menjadi nilai baru, jadi operator+ harus mengembalikan nilai baru. 3 Juga perhatikan bahwa operator+ menerima operan kirinya sebagai salinan , bukan sebagai referensi konstan. Alasan untuk ini sama dengan alasan yang menunjukkan bahwa untuk operator= argumennya diambil sebagai salinan.

Operator manipulasi bit ~ > | ^ << >> harus diimplementasikan dengan cara yang sama seperti operator aritmatika. Namun (dengan pengecualian overloading << dan >> untuk output dan input) ada beberapa kegunaan yang wajar untuk overloading.

3 Sekali lagi, pelajaran yang dapat dipetik dari ini adalah bahwa a += b biasanya lebih efektif daripada a + b dan harus lebih disukai jika memungkinkan.

Array berhitung

Operator indeks array adalah operator biner yang harus diimplementasikan sebagai anggota kelas. Ini digunakan untuk tipe kontainer yang memungkinkan Anda untuk mengakses elemen data Anda dengan kunci. Bentuk kanonik ketentuan mereka adalah sebagai berikut:

 class X { value_type operator[](index_type idx); const value_type operator[](index_type idx) const; // ... }; 

Jika Anda tidak ingin pengguna kelas Anda mengubah elemen data yang dikembalikan oleh operator[] (dalam hal ini, Anda dapat menghi>

Jika diketahui bahwa value_type merujuk ke tipe bawaan, varian const operator harus mengembalikan salinan alih-alih referensi const.

Operator untuk tipe penunjuk

Untuk menentukan iterator atau smart pointer Anda sendiri, Anda perlu membebani operator markup awalan unary * dan operator akses ke infix anggota penunjuk biner -> :

 class my_ptr { value_type operator*(); const value_type operator*() const; value_type* operator->(); const value_type* operator->() const; }; 

Harap dicatat bahwa mereka juga hampir selalu membutuhkan versi const dan non-konstanta. Untuk operator -> , jika value_type adalah class tipe (atau struct atau union ), operator->() lain operator->() disebut secara rekursif, sementara operator->() tidak mengembalikan nilai non-kelas.

Operator alamat unary tidak boleh kelebihan beban.

Untuk operator->*() lihat pertanyaan ini . Ini jarang digunakan dan, karena itu, jarang kelebihan beban. Bahkan, iterator sekalipun tidak membebani terlalu banyak.


Lanjutkan Operator Konversi

945
12 дек. jawabannya diberikan sbi 12 des. 2010-12-12 15:47 '10 pada 15:47 2010-12-12 15:47

Tiga aturan dasar untuk overloading operator di C ++

Dalam hal kelebihan operator di C ++, ada tiga aturan dasar yang harus Anda ikuti . Seperti halnya semua aturan semacam itu, memang ada pengecualian. Kadang-kadang orang menyimpang dari mereka, dan hasilnya adalah kode yang baik, tetapi penyimpangan positif seperti itu sangat sedikit. Setidaknya 99 dari 100 kelainan yang saya lihat tidak berdasar. Namun, bisa jadi sama dengan 999 dari 1000. Jadi, Anda sebaiknya mengikuti aturan berikut.

  • Kapan pun makna operator jelas tidak jelas dan tidak dapat disangkal, itu tidak boleh kelebihan beban. Sebagai gantinya, berikan fungsi dengan nama yang dipilih dengan baik. Pada prinsipnya, aturan pertama dan paling penting untuk kelebihan operator, di dalam hatinya, mengatakan: "Jangan lakukan ini. Ini mungkin tampak aneh karena banyak yang diketahui tentang kelebihan operator, dan karena itu banyak artikel, bab buku, dan teks-teks lain berkaitan dengan semua ini. Namun terlepas dari bukti yang nampak jelas ini, hanya ada beberapa kasus yang mengejutkan di mana operator kelebihan beban adalah tepat Alasannya adalah bahwa sebenarnya sulit untuk memahami semantik yang mendasari aplikasi operator jika operator menggunakan Aplikasi ene tidak terkenal dan tidak terbantahkan. Berlawanan dengan kepercayaan populer, ini hampir tidak pernah terjadi.

  • Selalu patuhi operator semantik terkenal.
    C ++ tidak membuat batasan pada semantik dari operator yang kelebihan beban. Kompiler Anda akan dengan senang hati menerima kode yang mengimplementasikan operator biner + untuk mengurangi operan kanannya. Namun, pengguna dari operator seperti itu tidak akan pernah mencurigai ungkapan a + b untuk mengurangi a dari b . Tentu saja, ini mengasumsikan bahwa semantik operator dalam domain aplikasi tidak dapat disangkal.

  • Selalu sediakan semua rangkaian operasi terkait.
    Operator terkait satu sama lain dan dengan operasi lain. Jika tipe Anda mendukung a + b , pengguna berharap mereka juga dapat memanggil a += b . Jika mendukung penambahan awalan ++a , mereka mengharapkan a++ juga berfungsi. Jika mereka dapat memeriksa apakah ada a < b , mereka mungkin juga dapat memeriksa apakah ada a > b . Jika mereka dapat menyalin-membangun tipe Anda, mereka mengharapkan penugasan untuk bekerja juga.


Lanjutkan keputusan antara anggota dan bukan anggota .

459
12 дек. jawabannya diberikan sbi 12 des. 2010-12-12 15:45 '10 pada 15:45 2010-12-12 15:45

Sintaks umum untuk operator kelebihan dalam C ++

Anda tidak dapat mengubah nilai operator untuk tipe bawaan di C ++, operator dapat kelebihan beban hanya untuk tipe khusus 1 . Artinya, setidaknya salah satu operan harus dari tipe yang ditentukan pengguna. Seperti fungsi kelebihan beban lainnya, operator dapat kelebihan beban hanya untuk satu set parameter tertentu.

Tidak semua operator dapat kelebihan beban di C ++. Di antara operator yang tidak bisa kelebihan beban :. :: sizeof typeid .* dan satu-satunya operator ternary di C ++, ?:

Di antara operator yang mungkin kelebihan beban di C ++ adalah sebagai berikut:

  • operator aritmatika: + - * / % dan += -= *= /= %= (semua infiks biner); + - (awalan unary); ++ -- (awalan dan postfix unary)
  • manipulasi bit: > | ^ << >> dan > |= ^= <<= >>= (semua infiks biner); ~ (awalan unary)
  • Aljabar Boolean: == != < > <= >= || > (semua infiks biner); ! (awalan unary)
  • Manajemen memori: new new[] delete delete[]
  • Operator konversi tersirat
  • koleksi: = [] -> ->* , (semua infiks biner); * > (semua awalan unary) () (panggilan fungsi, infiks n-ary)

Namun, fakta bahwa Anda dapat membebani semua ini tidak berarti Anda harus melakukannya. Lihat Aturan Kelebihan Operator Dasar.

Dalam C ++, operator kelebihan beban sebagai fungsi dengan nama khusus . Seperti dalam kasus fungsi lain, operator kelebihan beban biasanya dapat diimplementasikan baik sebagai fungsi anggota dari operan tipe kiri mereka , atau sebagai fungsi non-anggota . Terlepas dari apakah Anda dapat memilih atau menggunakan salah satunya, tergantung pada beberapa kriteria. 2 Operator unary @ 3 yang diterapkan pada objek x disebut sebagai operator@(x) atau sebagai x.operator@() . Operator infiks biner @ , diterapkan pada objek x dan y , disebut sebagai operator@(x,y) atau x.operator@(y) . 4

Operator yang diimplementasikan sebagai fungsi non-anggota terkadang adalah teman dari jenis operan mereka.

1 Istilah "yang ditentukan pengguna" bisa sedikit menyesatkan. C ++ membuat perbedaan antara tipe bawaan dan tipe yang ditentukan pengguna. Yang pertama termasuk, misalnya, int, char, dan double; yang terakhir mencakup semua jenis struktur, kelas, gabungan, dan enumerasi, termasuk dari perpustakaan standar, bahkan jika mereka tidak, dengan demikian, ditentukan oleh pengguna.

2 Ini dijelaskan di bagian selanjutnya dari FAQ ini.

3 @ bukan operator C ++ yang valid, jadi saya menggunakannya sebagai pengganti.

4 Operator ternary tunggal dalam C ++ tidak dapat kelebihan beban, dan operator n-ary tunggal harus selalu diimplementasikan sebagai fungsi anggota.


Lanjutkan Tiga aturan dasar untuk overloading operator di C ++ .

242
12 дек. jawabannya diberikan sbi 12 des. 2010-12-12 15:46 '10 pada 15:46 2010-12-12 15:46

Keputusan antara anggota dan bukan anggota

Biner operator = (penugasan), [] (ber>-> (akses anggota), dan operator n-ary () (panggilan fungsi) harus selalu diimplementasikan sebagai fungsi anggota , karena mereka memerlukan sintaks bahasa .

Operator lain dapat diimplementasikan sebagai anggota atau bukan anggota. Beberapa dari mereka, bagaimanapun, biasanya harus diimplementasikan sebagai fungsi non-anggota, karena operan kiri mereka tidak dapat diubah oleh Anda. Yang paling menonjol dari ini adalah operator input dan output << dan >> , yang operan kirinya adalah kelas stream dari perpustakaan standar yang tidak dapat Anda ubah.

Для всех операторов, где вам нужно либо реализовать их как функцию-член, либо не-членную функцию, использовать следующие правила большого пальца :

  • Если это унарный оператор , реализуйте его как функцию member .
  • Если двоичный оператор обрабатывает оба операнда одинаково (он оставляет их неизменными), реализуйте этот оператор как функцию не-член .
  • Если двоичный оператор не обрабатывает оба его операнда равно (обычно это изменяет его левый операнд), может быть полезно сделать его член функции его левого операнда типа, если он должен получить доступ к частным частям операнда.