Kelebihan operator: fungsi anggota vs. fungsi non-anggota?

121

Saya membaca bahwa operator yang kelebihan beban yang dideklarasikan sebagai fungsi anggota adalah asimetris karena hanya dapat memiliki satu parameter dan parameter lainnya yang dilewatkan secara otomatis adalah thispointer. Jadi tidak ada standar untuk membandingkannya. Di sisi lain, operator yang kelebihan beban dideklarasikan sebagai a friendadalah simetris karena kita melewatkan dua argumen dengan tipe yang sama dan karenanya, keduanya dapat dibandingkan.

Pertanyaan saya adalah bahwa ketika saya masih bisa membandingkan nilai pointer dengan referensi, mengapa teman lebih disukai? (menggunakan versi asimetris memberikan hasil yang sama dengan simetris) Mengapa algoritma STL hanya menggunakan versi simetris?

badmaash
sumber
11
Pertanyaan Anda sebenarnya hanya tentang operator biner. Tidak semua operator yang kelebihan beban dibatasi pada satu parameter. Operator () dapat menggunakan sejumlah parameter. Operator unary, di sisi lain, tidak dapat memiliki parameter apa pun.
Charles Salvia
4
Ini adalah salah satu dari banyak topik yang tercakup dalam C ++ FAQ: Operator overloading
Ben Voigt

Jawaban:

148

Jika Anda mendefinisikan fungsi operator yang kelebihan beban sebagai fungsi anggota, maka compiler menerjemahkan ekspresi seperti s1 + s2menjadi s1.operator+(s2). Itu berarti, fungsi anggota yang kelebihan beban operator akan dipanggil pada operan pertama. Begitulah cara kerja fungsi anggota!

Tetapi bagaimana jika operan pertama bukan kelas? Ada masalah besar jika kita ingin membebani operator di mana operan pertamanya bukan tipe kelas, lebih tepatnya double. Jadi Anda tidak bisa menulis seperti ini 10.0 + s2. Namun, Anda dapat menulis fungsi anggota yang kelebihan beban operator untuk ekspresi seperti s1 + 10.0.

Untuk mengatasi masalah pengurutan ini , kami mendefinisikan fungsi operator overloaded sebagai friendJIKA ia perlu mengakses privateanggota. Jadikan friendHANYA saat perlu mengakses anggota pribadi. Jika tidak, cukup buat fungsi non-teman non-anggota untuk meningkatkan enkapsulasi!

class Sample
{
 public:
    Sample operator + (const Sample& op2); //works with s1 + s2
    Sample operator + (double op2); //works with s1 + 10.0

   //Make it `friend` only when it needs to access private members. 
   //Otherwise simply make it **non-friend non-member** function.
    friend Sample operator + (double op1, const Sample& op2); //works with 10.0 + s2
}

Baca ini:
Sedikit masalah pemesanan di operan
Bagaimana Fungsi Non-Anggota Meningkatkan Enkapsulasi

Nawaz
sumber
2
"Buat friendhanya ketika perlu mengakses anggota pribadi .. dan ketika Anda tidak memiliki / bosan menulis aksesor, kan?
badmaash
4
@ Abhi: Pilih pilihan Anda: Peningkatan Enkapsulasi vs Kebiasaan menulis malas!
Nawaz
6
@ matthias, tidak semua operator bersifat komutatif. Contoh sederhananya adalah a/b.
edA-qa mort-ora-y
3
Cara umum untuk menghindari membuat operator non-anggota Anda membutuhkan friendadalah dengan menerapkannya dalam istilah operator penugasan operasi (yang hampir pasti akan menjadi anggota publik). Misalnya, Anda dapat mendefinisikan T T::operator+=(const T &rhs)sebagai anggota dan kemudian mendefinisikan non-anggota T operator(T lhs, const T &rhs)sebagai return lhs += rhs;. Fungsi non-anggota harus didefinisikan dalam namespace yang sama dengan kelas.
Adrian McCarthy
2
@ricky: Tapi jika lhs adalah salinan (seperti yang ada di komentar saya), maka fakta bahwa lhs berubah tidak masalah.
Adrian McCarthy
20

Ini belum tentu merupakan perbedaan antara friendkelebihan operator dan kelebihan operator fungsi anggota karena itu adalah antara kelebihan operator global dan kelebihan operator fungsi anggota.

Salah satu alasan untuk memilih kelebihan operator global adalah jika Anda ingin mengizinkan ekspresi di mana jenis kelas muncul di sisi kanan operator biner. Sebagai contoh:

Foo f = 100;
int x = 10;
cout << x + f;

Ini hanya berfungsi jika ada kelebihan beban operator global untuk

Operator Foo + (int x, const Foo & f);

Perhatikan bahwa kelebihan beban operator global tidak harus berupa friendfungsi. Ini hanya diperlukan jika memerlukan akses ke anggota pribadi dari Foo, tetapi tidak selalu demikian.

Terlepas dari itu, jika Foohanya operator fungsi anggota yang kelebihan beban, seperti:

class Foo
{
  ...
  Foo operator + (int x);
  ...
};

... maka kita hanya dapat memiliki ekspresi di mana sebuah Fooinstance muncul di sebelah kiri operator plus.

Charles Salvia
sumber
3
+1 untuk membuat perbedaan antara fungsi anggota dan fungsi non-anggota daripada fungsi anggota dan teman. Saya kira hari ini kita akan mengatakan "cakupan global atau namespace."
Adrian McCarthy