Kelebihan operator akses anggota ->,. *

129

Saya mengerti sebagian besar operator overloading, dengan pengecualian dari operator akses anggota ->, .*, ->*dll

Secara khusus, apa yang diteruskan ke fungsi operator ini, dan apa yang harus dikembalikan?

Bagaimana fungsi operator (mis. operator->(...)) Mengetahui anggota yang dirujuk? Bisakah itu tahu? Apakah itu perlu diketahui?

Akhirnya, adakah pertimbangan const yang perlu dipertimbangkan? Misalnya, ketika melakukan overload seperti operator[], umumnya Anda memerlukan versi const dan non-const. Apakah operator akses anggota memerlukan versi const dan non-const?

Bingo
sumber
1
Saya percaya C ++ di atas - Faq menyentuh semua Q yang ditanyakan di atas Q.
Alok Save
constdan yang bukan constversi operator->tidak diperlukan , tetapi menyediakan keduanya dapat bermanfaat.
Fred Foo
1
Lihat juga: yosefk.com/c++fqa/operator.html
György Andrasek
9
@Als: FAQ tidak menjelaskan cara kelebihan ->*dan .*. Bahkan, itu bahkan tidak menyebutkannya! Saya merasa mereka jarang berada di FAQ, tetapi saya dengan senang hati menghubungkan pertanyaan ini dari FAQ. Tolong jangan tutup ini sebagai duplikat dari FAQ!
sbi
@ sbi, saya baru saja gagal menemukan tautan ke pertanyaan ini dari FAQ (luar biasa) Anda, dan akhirnya mengajukan pertanyaan rangkap. Bisakah Anda membuatnya lebih jelas? (permintaan maaf jika sudah jelas).
P i

Jawaban:

144

->

Ini adalah satu-satunya yang sangat rumit. Ini harus menjadi fungsi anggota yang tidak statis, dan tidak membutuhkan argumen. Nilai kembali digunakan untuk melakukan pencarian anggota.

Jika nilai kembali adalah objek lain dari tipe kelas, bukan pointer, maka pencarian anggota berikutnya juga ditangani oleh suatu operator->fungsi. Inilah yang disebut "perilaku boros." Bahasa menyatukan operator->panggilan sampai panggilan terakhir mengembalikan sebuah pointer.

struct client
    { int a; };

struct proxy {
    client *target;
    client *operator->() const
        { return target; }
};

struct proxy2 {
    proxy *target;
    proxy &operator->() const
        { return * target; }
};

void f() {
    client x = { 3 };
    proxy y = { & x };
    proxy2 z = { & y };

    std::cout << x.a << y->a << z->a; // print "333"
}

->*

Yang ini hanya rumit karena tidak ada yang istimewa tentang itu. Versi non-overload membutuhkan objek pointer ke tipe kelas di sisi kiri dan objek pointer ke tipe anggota di sebelah kanan. Tetapi ketika Anda membebani secara berlebihan, Anda dapat mengambil argumen apa pun yang Anda suka dan mengembalikan apa pun yang Anda inginkan. Bahkan tidak harus menjadi anggota yang tidak statis.

Dengan kata lain, yang satu ini hanyalah sebuah operator biner normal seperti +, -, dan /. Lihat juga: Apakah operator gratis -> * kelebihan beban jahat?

.* dan .

Ini tidak dapat kelebihan beban. Sudah ada makna bawaan ketika sisi kiri adalah tipe kelas. Mungkin akan masuk akal jika kita dapat mendefinisikannya sebagai penunjuk di sebelah kiri, tetapi komite desain bahasa memutuskan bahwa akan lebih membingungkan daripada berguna.

Overloading ->, ->*, ., dan .*hanya dapat mengisi dalam kasus di mana ekspresi akan terdefinisi, tidak pernah mengubah arti dari ekspresi yang akan berlaku tanpa overloading.

Potatoswatter
sumber
2
Pernyataan terakhir Anda tidak sepenuhnya benar. Misalnya, Anda dapat membebani newoperator secara berlebihan , meskipun itu berlaku meskipun tidak kelebihan beban.
Matt
6
@Mat well, newselalu kelebihan beban, atau aturan kelebihan muatan tidak benar-benar berlaku untuk itu (13.5 / 5: Fungsi alokasi dan deallokasi, operator baru, operator baru [], operator hapus dan operator hapus [], dijelaskan sepenuhnya dalam 3.7 0,4. atribut dan pembatasan yang ditemukan di seluruh sub ayat ini tidak berlaku untuk mereka kecuali secara eksplisit dinyatakan dalam 3.7.4.) Tapi overloading unary &atau biner &&, ||atau ,, atau menambahkan overloads dari operator=, atau overloading apa saja untuk unscoped jenis pencacahan, dapat mengubah makna ekspresi. Klarifikasi pernyataan itu, terima kasih!
Potatoswatter
41

Operator -> spesial.

"Ia memiliki kendala tambahan yang tidak lazim: Ia harus mengembalikan objek (atau referensi ke objek) yang juga memiliki operator dereference pointer, atau harus mengembalikan pointer yang dapat digunakan untuk memilih apa yang ditunjuk oleh panah operator dereference pointer. " Bruce Eckel: Berpikir CPP Vol-one: operator->

Fungsionalitas tambahan disediakan untuk kenyamanan, jadi Anda tidak perlu menelepon

a->->func();

Anda bisa melakukannya:

a->func();

Itu membuat operator -> berbeda dari kelebihan operator lainnya.

Totonga
sumber
3
Jawaban ini layak mendapat kredit lebih, Anda dapat mengunduh buku Eckel dari tautan itu dan informasinya ada di bab 12 volume satu.
P i
26

Anda tidak dapat membebani akses anggota .(yaitu bagian kedua dari apa yang ->tidak). Namun Anda dapat membebani operator dereferencing unary *(yaitu bagian pertama dari apa yang ->tidak).

Operator C ++ ->pada dasarnya adalah gabungan dari dua langkah dan ini jelas jika Anda berpikir itu x->ysetara dengan (*x).y. C ++ memungkinkan Anda untuk menyesuaikan apa yang harus dilakukan dengan (*x)bagian tersebut ketika xmerupakan instance dari kelas Anda.

Semantik untuk ->overloading agak aneh karena C ++ memungkinkan Anda untuk mengembalikan pointer biasa (bahwa itu akan digunakan untuk menemukan objek runcing) atau untuk mengembalikan instance dari kelas lain jika kelas ini juga menyediakan ->operator. Ketika dalam kasus kedua ini pencarian untuk objek dereferensi berlanjut dari instance baru ini.

6502
sumber
2
Penjelasan hebat! Saya kira itu berarti sama untuk ->*, karena setara dengan bentuk (*x).*?
Bingo
10

The ->operator tidak tahu apa anggota yang sedang menunjuk, itu hanya menyediakan sebuah objek untuk melakukan akses anggota yang sebenarnya pada.

Selain itu, saya tidak melihat alasan mengapa Anda tidak dapat memberikan versi const dan non-const.

John Chadwick
sumber
7

Ketika Anda membebani operator -> () (tidak ada argumen yang dilewatkan di sini), apa yang sebenarnya dilakukan oleh kompiler adalah -> secara rekursif hingga mengembalikan pointer aktual ke suatu tipe. Kemudian menggunakan anggota / metode yang benar.

Ini berguna, misalnya, untuk membuat kelas pointer cerdas yang merangkum pointer sebenarnya. Operator yang kelebihan beban dipanggil>, melakukan apa pun yang dilakukannya (mis. Mengunci keamanan benang), mengembalikan pointer internal dan kemudian kompiler memanggil -> untuk pointer internal ini.

Adapun keteguhan - sudah dijawab dalam komentar dan jawaban lainnya (Anda bisa, dan harus, memberikan keduanya).

Asaf
sumber