Mengapa persahabatan tidak paling tidak diwariskan secara opsional dalam C ++? Saya memahami transitivitas dan refleksivitas dilarang karena alasan yang jelas (saya mengatakan ini hanya untuk menghindari jawaban kutipan FAQ sederhana), tetapi kurangnya sesuatu di sepanjang garis virtual friend class Foo;
teka - teki saya. Adakah yang tahu latar belakang sejarah di balik keputusan ini? Apakah persahabatan benar-benar hanya peretasan terbatas yang sejak itu menemukan jalannya ke dalam beberapa kegunaan terhormat yang tidak jelas?
Edit untuk klarifikasi: Saya sedang berbicara tentang skenario berikut, bukan di mana anak-anak A terpapar pada B atau B dan anaknya. Saya juga dapat membayangkan secara opsional memberikan akses ke penggantian fungsi teman, dll.
class A {
int x;
friend class B;
};
class B {
// OK as per friend declaration above.
void foo(A& a, int n) { a.x = n; }
};
class D : public B { /* can't get in A w/o 'friend class D' declaration. */ };
Jawaban yang diterima: seperti yang dinyatakan Loki , efek dapat disimulasikan lebih atau kurang dengan membuat fungsi proxy yang dilindungi di kelas dasar yang bersahabat , jadi tidak ada kebutuhan yang ketat untuk memberikan pertemanan ke kelas atau metode virtual heirarki. Saya tidak menyukai kebutuhan akan proxy boilerplate (yang menjadi basis teman secara efektif), tetapi saya kira ini dianggap lebih disukai daripada mekanisme bahasa yang kemungkinan besar akan disalahgunakan sebagian besar waktu. Saya pikir mungkin sudah waktunya saya membeli dan membaca The Design and Evolution of C ++ dari Stroupstrup , yang saya rekomendasikan oleh cukup banyak orang di sini, untuk mendapatkan wawasan yang lebih baik tentang jenis pertanyaan ini ...
Saya pikir jawaban untuk pertanyaan pertama Anda ada di pertanyaan ini: "Apakah teman ayahmu memiliki akses ke kemaluanmu?"
sumber
Kelas teman dapat mengekspos temannya melalui fungsi pengakses, dan kemudian memberikan akses melalui fungsi tersebut.
class stingy { int pennies; friend class hot_girl; }; class hot_girl { public: stingy *bf; int &get_cash( stingy &x = *bf ) { return x.pennies; } }; class moocher { public: // moocher can access stingy's pennies despite not being a friend int &get_cash( hot_girl &x ) { return x.get_cash(); } };
Ini memungkinkan kontrol yang lebih baik daripada transitivitas opsional. Misalnya,
get_cash
mungkinprotected
atau mungkin menegakkan protokol akses terbatas waktu proses.sumber
;)
C ++ Standard, bagian 11.4 / 8
Jika persahabatan akan diwarisi, maka kelas yang tidak dimaksudkan sebagai teman akan tiba-tiba memiliki akses ke internal kelas Anda dan melanggar enkapsulasi.
sumber
Karena itu tidak perlu.
Penggunaan file
friend
kata kunci itu sendiri mencurigakan. Dalam hal penggandengan, itu adalah hubungan terburuk (jauh di depan warisan dan komposisi).Setiap perubahan pada internal kelas berisiko berdampak pada teman di kelas ini ... apakah Anda benar-benar menginginkan jumlah teman yang tidak diketahui? Anda bahkan tidak akan dapat mencantumkannya jika mereka yang mewarisi dari mereka bisa menjadi teman juga, dan Anda berisiko memecahkan kode klien Anda setiap saat, tentunya ini tidak diinginkan.
Saya dengan bebas mengakui bahwa untuk pekerjaan rumah / proyek hewan peliharaan ketergantungan seringkali merupakan pertimbangan yang jauh. Pada proyek ukuran kecil tidak masalah. Tetapi segera setelah beberapa orang mengerjakan proyek yang sama dan ini berkembang menjadi puluhan ribu baris, Anda perlu membatasi dampak perubahan.
Ini membawa aturan yang sangat sederhana:
Mengubah internal kelas seharusnya hanya memengaruhi kelas itu sendiri
Tentu saja, Anda mungkin akan memengaruhi teman-temannya, tetapi ada dua kasus di sini:
std::ostream& operator<<(...)
sini, yang bukan anggota murni karena kebetulan aturan bahasaSaya akan merekomendasikan penggunaan metode sederhana:
class Example; class ExampleKey { friend class Example; ExampleKey(); }; class Restricted { public: void forExampleOnly(int,int,ExampleKey const&); };
Key
Pola sederhana ini memungkinkan Anda untuk menyatakan seorang teman (dengan cara tertentu) tanpa benar-benar memberinya akses ke internal Anda, sehingga mengisolasinya dari perubahan. Selanjutnya memungkinkan teman ini untuk meminjamkan kuncinya kepada wali (seperti anak-anak) jika diperlukan.sumber
Tebakan: Jika sebuah kelas mendeklarasikan kelas / fungsi lain sebagai teman, itu karena entitas kedua membutuhkan akses istimewa ke yang pertama. Apa gunanya memberikan entitas kedua akses istimewa ke sejumlah kelas yang diturunkan dari yang pertama?
sumber
B
akan memiliki akses ke semua kelas yang diwarisi dariA
...Kelas turunan hanya dapat mewarisi sesuatu, yaitu 'anggota' basis. Pernyataan teman bukanlah anggota kelas pertemanan.
dan selanjutnya
Karena 'teman' bukanlah anggota kelas dasar, bagaimana bisa diturunkan oleh kelas turunan?
sumber
Fungsi teman di kelas menetapkan properti eksternal ke fungsi tersebut. yaitu extern berarti bahwa fungsi tersebut telah dideklarasikan dan didefinisikan di suatu tempat di luar kelas.
Oleh karena itu, artinya fungsi teman bukan anggota kelas. Jadi, warisan hanya memungkinkan Anda untuk mewarisi properti kelas, bukan hal eksternal. Dan juga jika warisan diperbolehkan untuk fungsi teman, maka kelas pihak ketiga mewarisi.
sumber
Teman baik dalam hal warisan seperti antarmuka gaya untuk wadah Tapi bagi saya, sebagai kata pertama, C ++ tidak memiliki warisan yang dapat disebarkan
class Thing; //an interface for Thing container's struct IThing { friend Thing; protected: int IThing_getData() = 0; }; //container for thing's struct MyContainer : public IThing { protected: //here is reserved access to Thing int IThing_getData() override {...} }; struct Thing { void setYourContainer(IThing* aContainerOfThings) { //access to unique function in protected area aContainerOfThings->IThing_getData(); //authorized access } }; struct ChildThing : public Thing { void doTest() { //here the lack of granularity, you cannot access to the container. //to use the container, you must implement all //function in the Thing class aContainerOfThings->IThing_getData(); //forbidden access } };
Bagi saya masalah C ++ adalah kurangnya perincian yang sangat baik untuk mengontrol semua akses dari mana saja untuk apa pun:
teman Hal dapat menjadi Hal teman. * untuk memberikan akses ke semua anak Benda
Dan lebih banyak lagi, teman [area bernama] Hal. * Untuk memberikan akses yang tepat berada di kelas Kontainer melalui area bernama khusus untuk teman.
Ok hentikan mimpinya. Tapi sekarang, Anda tahu kegunaan teman yang menarik.
Di urutan lain, Anda juga bisa menemukan hal menarik untuk diketahui semua kelas ramah terhadap diri sendiri. Dengan kata lain, instance kelas dapat memanggil semua
anggota instance lain dengan nama yang sama tanpa batasan:
class Object { private: void test() {} protected: void callAnotherTest(Object* anotherObject) { //private, but yes you can call test() from //another object instance anotherObject)->test(); } };
sumber
Logika sederhana: 'Saya punya teman Jane. Hanya karena kita menjadi teman kemarin tidak membuat semua temannya menjadi milikku. '
Saya masih perlu menyetujui persahabatan individu itu, dan tingkat kepercayaannya akan sesuai.
sumber