Jadi saya perhatikan mungkin untuk menghindari menempatkan fungsi pribadi di header dengan melakukan sesuatu seperti ini:
// In file pred_list.h:
class PredicateList
{
int somePrivateField;
friend class PredicateList_HelperFunctions;
public:
bool match();
}
// In file pred_list.cpp:
class PredicateList_HelperFunctions
{
static bool fullMatch(PredicateList& p)
{
return p.somePrivateField == 5; // or whatever
}
}
bool PredicateList::match()
{
return PredicateList_HelperFunctions::fullMatch(*this);
}
Fungsi pribadi tidak pernah dideklarasikan di header, dan konsumen kelas yang mengimpor header tidak perlu tahu itu ada. Ini diperlukan jika fungsi helper adalah templat (alternatifnya adalah meletakkan kode lengkap di header), itulah cara saya "menemukan" ini. Kelebihan bagus lainnya dari tidak perlu mengkompilasi ulang setiap file yang menyertakan header jika Anda menambah / menghapus / memodifikasi fungsi anggota pribadi. Semua fungsi pribadi ada dalam file .cpp.
Begitu...
- Apakah ini pola desain terkenal yang ada namanya?
- Bagi saya (berasal dari latar belakang Java / C # dan belajar C ++ pada waktu saya sendiri), ini sepertinya hal yang sangat baik, karena header mendefinisikan antarmuka, sedangkan .cpp mendefinisikan implementasi (dan waktu kompilasi yang ditingkatkan adalah bonus yang bagus). Namun, baunya seperti menyalahgunakan fitur bahasa yang tidak dimaksudkan untuk digunakan seperti itu. Jadi, mana yang benar? Apakah ini sesuatu yang Anda tidak suka melihat dalam proyek C ++ profesional?
- Adakah jebakan yang tidak saya pikirkan?
Saya menyadari Pimpl, yang merupakan cara yang jauh lebih kuat untuk menyembunyikan implementasi di tepi perpustakaan. Ini lebih untuk digunakan dengan kelas internal, di mana Pimpl akan menyebabkan masalah kinerja, atau tidak berfungsi karena kelas perlu diperlakukan sebagai nilai.
EDIT 2: Jawaban sempurna Dragon Energy di bawah ini menyarankan solusi berikut, yang sama sekali tidak menggunakan friend
kata kunci:
// In file pred_list.h:
class PredicateList
{
int somePrivateField;
class Private;
public:
bool match();
}
// In file pred_list.cpp:
class PredicateList::Private
{
public:
static bool fullMatch(PredicateList& p)
{
return p.somePrivateField == 5; // or whatever
}
}
bool PredicateList::match()
{
return PredicateList::Private::fullMatch(*this);
}
Ini menghindari faktor kejut dari friend
(yang tampaknya telah di-iblis seperti goto
) sambil tetap mempertahankan prinsip pemisahan yang sama.
sumber
Jawaban:
Agak terlalu esoteris untuk mengatakan setidaknya ketika Anda sudah mengenali yang mungkin membuat saya menggaruk-garuk kepala sejenak ketika saya pertama kali mulai menemukan kode Anda bertanya-tanya apa yang Anda lakukan dan di mana kelas-kelas pembantu ini diterapkan sampai saya mulai mengambil gaya Anda / Kebiasaan (pada titik mana saya mungkin akan benar-benar terbiasa).
Saya suka Anda mengurangi jumlah informasi di header. Terutama dalam basis kode yang sangat besar, yang dapat memiliki efek praktis untuk mengurangi dependensi waktu kompilasi dan akhirnya membangun waktu.
Reaksi usus saya adalah bahwa jika Anda merasa perlu untuk menyembunyikan detail implementasi dengan cara ini, untuk mendukung parameter lewat ke fungsi yang berdiri sendiri dengan tautan internal dalam file sumber. Biasanya Anda dapat mengimplementasikan fungsi utilitas (atau seluruh kelas) yang berguna untuk mengimplementasikan kelas tertentu tanpa memiliki akses ke semua internal kelas dan alih-alih hanya meneruskan yang relevan dari penerapan metode ke fungsi (atau konstruktor). Dan tentu saja itu memiliki bonus mengurangi kopling antara kelas Anda dan "pembantu". Ini juga memiliki kecenderungan untuk menggeneralisasi apa yang seharusnya menjadi "pembantu" lebih lanjut jika Anda menemukan bahwa mereka mulai melayani tujuan yang lebih umum yang berlaku untuk implementasi lebih dari satu kelas.
Jika itu menjadi sulit, saya akan mempertimbangkan solusi kedua, lebih idiomatis yang merupakan jerawat (saya menyadari Anda menyebutkan masalah dengan itu tetapi saya pikir Anda dapat menggeneralisasi solusi untuk menghindari mereka dengan usaha minimal). Itu bisa memindahkan seluruh banyak informasi yang perlu diimplementasikan oleh kelas Anda termasuk data privatnya dari grosir header. Masalah kinerja pimpl sebagian besar dapat dikurangi dengan pengalokasi waktu-murah konstan * seperti daftar gratis sambil menjaga semantik nilai tanpa harus menerapkan ctor copy yang ditentukan pengguna full-blown.
Secara pribadi hanya setelah menguras kemungkinan itu saya akan mempertimbangkan sesuatu seperti ini. Saya pikir itu ide yang baik jika alternatifnya seperti metode yang lebih pribadi yang diekspos ke header dengan mungkin hanya sifat esoterik yang menjadi perhatian praktis.
Sebuah alternatif
Salah satu alternatif yang muncul di kepala saya sekarang yang sebagian besar mencapai tujuan yang sama Anda tidak ada teman adalah seperti ini:
Nah, itu mungkin tampak seperti perbedaan yang sangat bisa diperdebatkan dan saya masih menyebutnya "penolong" (dalam arti yang mungkin merendahkan karena kita masih menyerahkan seluruh keadaan internal kelas ke fungsi apakah perlu semuanya atau tidak) kecuali itu menghindari faktor "kejutan" dari pertemuan
friend
. Secara umumfriend
terlihat agak menakutkan untuk melihat sering tidak ada inspeksi lebih lanjut, karena ia mengatakan bahwa internal kelas Anda dapat diakses di tempat lain (yang membawa implikasi bahwa ia mungkin tidak mampu mempertahankan invariannya sendiri). Dengan cara Anda menggunakannyafriend
menjadi agak diperdebatkan jika orang menyadari praktik sejak saat itufriend
hanya berada di file sumber yang sama yang membantu mengimplementasikan fungsionalitas pribadi kelas, tetapi hal di atas menghasilkan banyak efek yang sama setidaknya dengan manfaat yang mungkin diperdebatkan bahwa itu tidak melibatkan teman yang menghindari semua jenis itu ("Oh tembak, kelas ini punya teman. Di mana lagi privatnya bisa diakses / dimutasi? "). Sedangkan versi di atas segera mengkomunikasikan bahwa tidak ada cara bagi privat untuk diakses / dimutasi di luar apa pun yang dilakukan dalam implementasiPredicateList
.Itu mungkin bergerak menuju wilayah yang agak dogmatis dengan tingkat nuansa ini karena siapa pun dapat dengan cepat mengetahui apakah Anda secara seragam menyebutkan berbagai hal
*Helper*
dan menempatkan semuanya dalam file sumber yang sama yang semuanya dibundel bersama sebagai bagian dari implementasi privat sebuah kelas. Tetapi jika kita menjadi rewel, mungkin gaya yang tepat di atas tidak akan menyebabkan reaksi spontan sekilas tidak adafriend
kata kunci yang cenderung terlihat sedikit menakutkan.Untuk pertanyaan lain:
Itu mungkin merupakan kemungkinan lintas batas API di mana klien dapat menentukan kelas kedua dengan nama yang sama dan mendapatkan akses ke internal seperti itu tanpa kesalahan tautan. Kemudian lagi saya sebagian besar C coder bekerja di grafik di mana masalah keamanan pada tingkat "bagaimana jika" ini sangat rendah pada daftar prioritas, jadi kekhawatiran seperti ini hanya yang saya cenderung melambaikan tangan dan menari dan cobalah untuk berpura-pura seolah mereka tidak ada. :-D Jika Anda bekerja di domain di mana masalah seperti ini agak serius, saya pikir itu pertimbangan yang layak untuk dibuat.
Usulan alternatif di atas juga menghindari masalah ini. Jika Anda masih ingin tetap menggunakan
friend
, Anda juga dapat menghindari masalah itu dengan menjadikan helper sebagai kelas bersarang pribadi.Tidak ada yang sepengetahuan saya. Saya agak ragu akan ada satu karena itu benar-benar masuk ke detail detail dan gaya implementasi.
"Helper Hell"
Saya mendapat permintaan klarifikasi lebih lanjut tentang bagaimana saya terkadang merasa ngeri ketika saya melihat implementasi dengan banyak kode "pembantu", dan itu mungkin sedikit kontroversial dengan beberapa tetapi sebenarnya faktual karena saya benar-benar merasa ngeri ketika saya debug beberapa implementasi kolega saya di kelas hanya untuk menemukan banyak "pembantu". :-D Dan aku bukan satu-satunya di tim yang menggaruk-garuk kepalaku mencoba untuk mencari tahu apa yang seharusnya dilakukan semua penolong ini. Saya juga tidak ingin lepas dari dogmatis seperti "Jangan menggunakan pembantu," tetapi saya akan membuat saran kecil bahwa mungkin membantu untuk memikirkan bagaimana menerapkan hal-hal yang tidak ada pada mereka ketika praktis.
Dan ya, saya termasuk metode pribadi. Jika saya melihat kelas dengan antarmuka publik yang langsung tetapi seperti serangkaian metode pribadi yang tak terbatas yang agak tidak jelas tujuannya seperti
find_impl
ataufind_detail
ataufind_helper
, maka saya juga merasa ngeri dengan cara yang sama.Apa yang saya sarankan sebagai alternatif adalah fungsi non-teman nonanggota dengan tautan internal (dideklarasikan
static
atau di dalam namespace anonim) untuk membantu mengimplementasikan kelas Anda dengan setidaknya tujuan yang lebih umum daripada "fungsi yang membantu mengimplementasikan orang lain". Dan saya dapat mengutip Herb Sutter dari C ++ 'Coding Standards' di sini untuk alasan mengapa itu lebih disukai dari sudut pandang SE umum:Anda juga dapat memahami "biaya keanggotaan" yang dibicarakannya sampai batas tertentu dalam hal prinsip dasar penyempitan ruang lingkup variabel. Jika Anda membayangkan, sebagai contoh paling ekstrem, objek Dewa yang memiliki semua kode yang diperlukan untuk menjalankan seluruh program Anda, maka pilih "pembantu" jenis ini (fungsi, apakah fungsi anggota atau teman) yang dapat mengakses semua internal ( privat) dari suatu kelas pada dasarnya membuat variabel-variabel itu tidak kurang bermasalah daripada variabel global. Anda memiliki semua kesulitan mengelola keadaan dengan benar dan menjaga keamanan serta mempertahankan invarian yang akan Anda dapatkan dengan variabel global dalam contoh paling ekstrem ini. Dan tentu saja sebagian besar contoh nyata mudah-mudahan tidak mendekati ekstrem ini, tetapi menyembunyikan informasi hanya berguna karena membatasi ruang lingkup informasi yang diakses.
Sekarang Sutter sudah memberikan penjelasan yang bagus di sini, tetapi saya juga menambahkan bahwa decoupling cenderung mempromosikan seperti peningkatan psikologis (setidaknya jika otak Anda bekerja seperti milik saya) dalam hal bagaimana Anda merancang fungsi. Ketika Anda mulai mendesain fungsi yang tidak dapat mengakses semua yang ada di kelas kecuali hanya parameter yang relevan yang Anda lewati atau, jika Anda lulus instance dari kelas sebagai parameter, hanya anggota publiknya, ia cenderung mempromosikan pola pikir desain yang mendukung fungsi yang memiliki tujuan yang lebih jelas, di atas decoupling dan mempromosikan enkapsulasi yang lebih baik, daripada apa yang Anda mungkin tergoda untuk merancang jika Anda bisa mengakses semuanya.
Jika kita kembali ke ekstremitas maka basis kode yang penuh dengan variabel global tidak benar-benar menggoda pengembang untuk merancang fungsi dengan cara yang jelas dan digeneralisasi untuk tujuan tertentu. Sangat cepat, semakin banyak informasi yang dapat Anda akses dalam suatu fungsi, semakin banyak dari kita manusia menghadapi godaan untuk merosotkannya dan mengurangi kejernihannya dalam hal mengakses semua informasi tambahan yang kita miliki daripada menerima parameter yang lebih spesifik dan relevan dengan fungsi itu. untuk mempersempit aksesnya ke negara dan memperluas penerapannya dan meningkatkan kejelasan niatnya. Itu berlaku (meskipun umumnya pada tingkat yang lebih rendah) dengan fungsi anggota atau teman.
sumber
PredicateList
, sering kali mungkin layak untuk hanya meneruskan satu atau dua anggota dari daftar predikat ke fungsi yang sedikit lebih umum yang tidak memerlukan akses ke setiap anggota pribadiPredicateList
, dan seringkali yang cenderung cenderung juga menghasilkan nama dan tujuan yang lebih jelas dan lebih umum untuk fungsi internal itu serta lebih banyak peluang untuk "penggunaan kembali kode belakang".