Apa gunanya menggunakan kelas teman C ++?

8

Saya mencoba memahami teman C ++. Kapan kasus penggunaan yang baik untuk menggunakan teman? Saya kira jika kita ingin membiarkan kelas lain memiliki akses ke atribut kelas lain, mengapa kita tidak menjadikannya sebagai publik atau mewarisi dari kelas itu saja?

Terima kasih untuk bantuannya.

Joshua Partogi
sumber
4
Saya pikir pertanyaan ini jelas milik di sini karena lebih dari "mengapa?" pertanyaan daripada "bagaimana?" pertanyaan, dan sudah ada beberapa poin bagus yang dibuat dalam jawaban.
Larry Coleman

Jawaban:

14

Membuat anggota kelas publicberarti memberi semua orang akses ke sana, sehingga melanggar enkapsulasi sepenuhnya.

Mewarisi dari sebuah kelas seringkali tidak diinginkan, jika kelas teman tidak dimaksudkan untuk menjadi subclass. Subkelas hanya untuk mendapatkan akses ke internal kelas adalah kesalahan desain yang serius. Dan bahkan subclass tidak dapat melihat anggota pribadi dari kelas dasar.

Penggunaan tipikal friendadalah untuk operator yang tidak dapat menjadi anggota, seperti operator aliran, operator+dll. Dalam kasus ini, kelas aktual yang terkait dengan mereka bukan (selalu) parameter pertama dari fungsi, sehingga fungsi tidak dapat diimplementasikan sebagai metode anggota.

Contoh lain adalah menerapkan iterator untuk koleksi. Iterator (dan hanya iterator) perlu melihat bagian dalam koleksi induknya, namun itu bukan subkelas dari koleksi.

Péter Török
sumber
Kadang-kadang, fungsi teman gratis itu didefinisikan sebaris di kelas.
Deduplicator
3

Contoh saya paling sering melihat kelas teman yang digunakan dalam C ++ adalah dalam unit testing. Tes unit biasanya ingin mengetahui semua tentang internal Anda, tetapi bukan bagian dari Anda, dan tidak masuk akal untuk meminta mereka mencoba mewarisi dari Anda.

Jika Anda tidak terbiasa dengan penulisan unit test, saya sarankan Anda segera mulai .

btilly
sumber
6
Kelas produksi harus tahu apa-apa tentang kelas unit test. Dan tes tidak perlu melihat internal kelas yang diuji; kelas yang dirancang dengan baik dapat diuji unit melalui API publiknya. Jika tidak, kemungkinan besar bukan kelas yang dirancang dengan baik (misalnya ia mencoba melakukan terlalu banyak, sehingga bagian dari fungsinya harus lebih baik diekstraksi ke dalam kelas yang terpisah).
Péter Török
4
@ peter-torok: Itu tergantung pada arti "dirancang dengan baik" bagi Anda. Kasus umum di mana saya ingin akses istimewa adalah menyuntikkan ejekan untuk melacak apakah tindakan yang seharusnya terjadi memang dilakukan. Mengekspos bagian-bagian dari struktur objek di API publik sepertinya merupakan pelanggaran utama enkapsulasi. Oleh karena itu saya sering ingin kode uji unit memiliki akses istimewa.
btilly
1
@ btilly: Jika Anda ingin menyuntikkan tiruan, Anda dapat menggunakan parameter di konstruktor, tidak perlu mengakses internal kelas.
Giorgio
1
@ PéterTörök GTest tidak setuju dengan Anda. Ada beberapa kasus di mana tes teman akan membantu - misalnya seseorang mungkin ingin membandingkan kesetaraan dua objek dari kelas yang sama tanpa memaparkan operasi kesetaraan di API (mungkin untuk masalah keamanan).
VF1
1
@Iorgio Ya, Anda dapat membuat informasi yang ingin Anda uji unit menjadi publik. Tapi sekarang API publik Anda baru saja tumbuh, dan dapat mengunci Anda untuk merancang keputusan yang tidak bijaksana di masa depan. Dengan kata lain, adalah tepat untuk memiliki unit test untuk kasus-kasus tepi implementasi Anda saat ini. Itu tidak selalu tepat untuk mengekspos tingkat detail kepada konsumen kelas Anda.
btilly
0

Pertanyaan ini harus di stackoverflow. Pokoknya Anda menggunakan teman hanya ketika Anda ingin berbagi bagian pribadi dari kelas Anda dengan kelas lain (atau kelas) tetapi tidak dengan orang lain. Jika Anda membuatnya publik semua orang dapat melihat bagian pribadi Anda (pun intended ;-P). Ada dua batasan penting yang menegakkan privasi: 1) Anda harus menentukan siapa teman Anda. Tidak ada orang lain yang bisa menjadi teman. 2) Anda tidak dapat mewarisi perilaku "ramah" di subclass kelas teman

DPD
sumber
1
Metode pertanyaan Sokrates: Dan mengapa orang lain melihat bagian pribadi Anda buruk?
Larry Coleman
3
@Larry: tergantung apakah dilakukan dengan selera tinggi, atau untuk nilai kejutan: D
Matt Ellen
@ Matt: jadi apakah ada penggunaan variabel publik yang enak?
Larry Coleman
@Larry: (non-halus) ketika mengubah keadaan objek, di luar kendali objek, tidak akan menyebabkan objek berada dalam keadaan tidak valid. Saya pikir peristiwa dalam c # hitung dalam hal ini.
Matt Ellen
0

Penggunaan khas adalah untuk memberikan akses ke fungsi yang membentuk bagian dari antarmuka kelas, tetapi berkat aturan lain tidak dapat benar-benar menjadi bagian dari kelas yang semestinya. Inserters / extractors untuk iostreams adalah contoh klasik:

namespace whatever { 
    class something { 
        // ...    
        friend std::ostream &operator<<(std::ostream &os, something const &thing);
        friend std::istream &operator>>(std::istream &is, something &thing);
    };
}

Menjadikan para operator ini anggota kelas tidak akan bekerja. Operasi I / O terlihat seperti:

whatever::something thing;

std::cout << thing;

Untuk operator yang diimplementasikan sebagai fungsi anggota, ini akan diselesaikan sebagai:

std::cout.operator<<(thing);

Yaitu, fungsi harus menjadi anggota std::cout, bukan dari something. Karena kami tidak ingin std::ostreamterus-menerus memodifikasi , satu-satunya pilihan kami yang masuk akal adalah membebani operator dengan fungsi bebas alih-alih fungsi anggota. Itu membuat kita memiliki dua kemungkinan: mengabaikan enkapsulasi sepenuhnya, dan membuat segala sesuatu di kelas publik, atau menjaganya tetap pribadi, tetapi memberikan akses ke beberapa hal yang benar-benar membutuhkannya.

Membuat kelas lain menjadi teman agak kurang umum. Dalam hal ini, Anda biasanya membuat sesuatu berdasarkan urutan modul atau subsistem - sekumpulan kelas yang bekerja bersama dan memiliki beberapa tingkat akses khusus satu sama lain yang tidak diberikan kepada dunia secara luas.

Jerry Coffin
sumber
0

Ada contoh yang cukup bagus tentang perlunya kelas teman dalam permintaan ini untuk kelas seperti teman di C # , bahasa yang tidak memilikinya.

Dikutip grosir di sini:

Akses pengubah dalam .NET dirancang dengan gagasan bahwa Anda dapat memercayai rekan kerja Anda untuk mengetahui cara kerja kode dan karenanya tidak lolos dan memperkenalkan bug.

Namun gagasan ini cacat. Pengembangan perangkat lunak sangat rumit sehingga programmer yang berpengalaman tahu untuk tidak mempercayai dirinya sendiri! Untuk alasan ini, pemrogram berpengalaman lebih suka menulis kode yang, menurut desain, tidak dapat dipecahkan melalui penyalahgunaan yang tidak disengaja. .NET menyediakan alat untuk melakukan ini ketika hanya satu kelas yang terlibat, tetapi saat seorang programmer mencoba untuk membuat set bukti-peluru dari dua atau tiga kelas yang berinteraksi, ini rusak.

Pendekatan .NET yang dimaksudkan untuk ini adalah untuk menandai anggota bersama sebagai "internal", yang memungkinkan kelas untuk berinteraksi dengan internal masing-masing. Sayangnya ini juga memungkinkan setiap kelas lain di majelis untuk mengacaukan internal, baik itu secara tidak sengaja atau sengaja.

Mads Torgersen, C # Language PM, bahkan telah menjawab proposal ini, menyatakan bahwa kekhawatiran tersebut memang benar, tetapi tidak yakin tentang implementasi spesifik yang disarankan di sana.

C ++ friendhanyalah salah satu cara untuk mengatasi masalah yang dijelaskan.

enverpex
sumber
0

Ketika Anda ingin kelas Anda memiliki hubungan monogami.

Misalnya ketika Anda tidak ingin kelas yang tidak dikenal mengakses privasi mereka, tetapi pasangan mereka membutuhkan akses.

Danny Varod
sumber
Kasus penggunaan yang serupa dengan kasus internal di C # dan paket di Jawa.
Danny Varod
0

Saya menemukan kelas teman di C ++ berguna di mana di Jawa saya akan menggunakan akses paket. Yaitu, saya memiliki banyak kelas yang sangat terkait sehingga ditulis dan dikelola sebagai satu unit, dalam file sumber yang sama. Siapa pun yang bekerja pada salah satu dari mereka benar-benar mengerjakan semuanya. Invarian yang mereka pertahankan dipertahankan bersama. Masuk akal bagi mereka untuk melihat internal satu sama lain sehingga mereka dapat mempertahankan invarian bersama mereka.

Yang tidak masuk akal adalah akses yang dilindungi. Jika tidak aman untuk mengekspos beberapa bagian dari internal saya kepada publik, itu tidak aman untuk mengeksposnya ke jackanapes apa pun yang muncul kemudian dalam proyek yang sama sekali tidak terkait dan mengklaim sebagai subclass saya. Biarkan subclass menggunakan antarmuka publik saya, sama seperti orang lain. (Kecuali mereka teman, tetapi dalam kasus itu saya menulisnya dan mengimplementasikannya di sumber yang sama, jadi mereka hampir tidak dari proyek lain.)

Ganbustein
sumber
-1

Katakanlah ada fungsi eksternal yang mengharuskan Anda untuk melakukan operasi pada anggota pribadi atau yang dilindungi dari dua kelas yang berbeda dalam program Anda. sekarang, agar fungsi itu mendapatkan akses dan benar-benar menggunakan anggota kelas ini, Anda harus menjadikannya teman dari kedua kelas. Perlu diingat bahwa dengan menjadikannya teman dari kedua kelas ini tidak menjadikannya sebagai anggota kelas, ia hanya mendapatkan prevelage untuk merujuk pada anggota pribadi atau yang dilindungi dari kelas-kelas ini. Saya dapat mengatakan bahwa Anda perlu menggunakan fungsi teman saat Anda ingin mengoperasikan objek dari dua kelas yang berbeda. Padahal terlalu seringnya fungsi teman dan kelas bisa mengurangi nilai enkapsulasi.

Fiko V
sumber
2
posting ini agak sulit dibaca (dinding teks). Maukah Anda mengeditnya menjadi bentuk yang lebih baik?
nyamuk