Saya bertanya-tanya apa yang membuat seorang programmer memilih idiom Pimpl atau kelas virtual murni dan warisan.
Saya memahami bahwa idiom pimpl hadir dengan satu tipuan ekstra eksplisit untuk setiap metode publik dan overhead pembuatan objek.
Kelas virtual murni di sisi lain hadir dengan indireksi implisit (vtable) untuk implementasi yang mewarisi dan saya memahami bahwa tidak ada overhead pembuatan objek.
EDIT : Tetapi Anda membutuhkan pabrik jika Anda membuat objek dari luar
Apa yang membuat kelas virtual murni kurang diminati dibandingkan idiom pimpl?
c++
abstract-class
pimpl-idiom
Arkaitz Jimenez
sumber
sumber
Jawaban:
Saat menulis kelas C ++, sebaiknya pikirkan apakah kelas itu akan dibuat
Jenis Nilai
Disalin berdasarkan nilai, identitas tidak pernah penting. Ini tepat untuk menjadi kunci dalam peta std ::. Contoh, kelas "string", atau kelas "tanggal", atau kelas "bilangan kompleks". Untuk "menyalin" contoh kelas seperti itu masuk akal.
Jenis Entitas
Identitas itu penting. Selalu diteruskan oleh referensi, tidak pernah dengan "nilai". Seringkali, tidak masuk akal untuk "menyalin" contoh kelas sama sekali. Jika memang masuk akal, metode "Klon" polimorfik biasanya lebih tepat. Contoh: Kelas Socket, kelas Database, kelas "kebijakan", apa pun yang akan menjadi "penutup" dalam bahasa fungsional.
Baik pImpl dan kelas dasar abstrak murni adalah teknik untuk mengurangi ketergantungan waktu kompilasi.
Namun, saya hanya pernah menggunakan pImpl untuk mengimplementasikan tipe Nilai (tipe 1), dan hanya kadang-kadang ketika saya benar-benar ingin meminimalkan ketergantungan kopling dan waktu kompilasi. Seringkali, itu tidak sepadan dengan repotnya. Seperti yang Anda tunjukkan dengan benar, ada lebih banyak overhead sintaksis karena Anda harus menulis metode penerusan untuk semua metode publik. Untuk kelas tipe 2, saya selalu menggunakan kelas dasar abstrak murni dengan metode pabrik terkait.
sumber
Pointer to implementation
biasanya tentang menyembunyikan detail implementasi struktural.Interfaces
adalah tentang memberi contoh penerapan yang berbeda. Mereka benar-benar melayani dua tujuan berbeda.sumber
Idiom pimpl membantu Anda mengurangi dependensi dan waktu build terutama dalam aplikasi besar, dan meminimalkan eksposur header dari detail implementasi kelas Anda ke satu unit kompilasi. Pengguna kelas Anda bahkan tidak perlu menyadari keberadaan jerawat (kecuali sebagai penunjuk samar yang tidak mereka ketahui!).
Kelas abstrak (virtual murni) adalah sesuatu yang harus diperhatikan klien Anda: jika Anda mencoba menggunakannya untuk mengurangi referensi penggandengan dan melingkar, Anda perlu menambahkan beberapa cara untuk memungkinkan mereka membuat objek (misalnya melalui metode pabrik atau kelas, injeksi ketergantungan atau mekanisme lainnya).
sumber
Saya sedang mencari jawaban untuk pertanyaan yang sama. Setelah membaca beberapa artikel dan beberapa latihan, saya lebih suka menggunakan "antarmuka kelas virtual murni" .
Satu-satunya kelemahan ( saya mencoba untuk menyelidiki ini ) adalah idiom pimpl bisa lebih cepat
sumber
Saya benci jerawat! Mereka mengerjakan kelas dengan jelek dan tidak bisa dibaca. Semua metode diarahkan ke jerawat. Anda tidak pernah melihat di header, fungsionalitas apa yang memiliki kelas tersebut, sehingga Anda tidak dapat memfaktorkan ulangnya (misalnya, cukup ubah visibilitas metode). Kelas terasa seperti "hamil". Saya pikir menggunakan iterfaces lebih baik dan benar-benar cukup untuk menyembunyikan implementasi dari klien. Anda dapat membiarkan satu kelas menerapkan beberapa antarmuka untuk menahannya. Seseorang harus lebih memilih antarmuka! Catatan: Anda tidak perlu kelas pabrik. Relevan adalah bahwa klien kelas berkomunikasi dengan instansinya melalui antarmuka yang sesuai. Menyembunyikan metode pribadi yang saya temukan sebagai paranoia yang aneh dan tidak melihat alasan untuk ini karena kami memiliki antarmuka.
sumber
Ada masalah yang sangat nyata dengan pustaka bersama yang dielakkan oleh idiom pimpl dengan rapi yang tidak dapat dilakukan oleh virtual murni: Anda tidak dapat dengan aman mengubah / menghapus anggota data kelas tanpa memaksa pengguna kelas untuk mengkompilasi ulang kode mereka. Itu mungkin dapat diterima dalam beberapa keadaan, tetapi tidak misalnya untuk perpustakaan sistem.
Untuk menjelaskan masalah secara mendetail, pertimbangkan kode berikut di pustaka / header bersama Anda:
Kompilator memancarkan kode di pustaka bersama yang menghitung alamat integer yang akan diinisialisasi menjadi offset tertentu (mungkin nol dalam kasus ini, karena itu satu-satunya anggota) dari penunjuk ke objek A yang diketahuinya
this
.Di sisi pengguna kode,
new A
pertama-tama akan mengalokasikansizeof(A)
byte memori, kemudian menyerahkan pointer ke memori itu keA::A()
konstruktor sebagaithis
.Jika dalam revisi perpustakaan Anda nanti Anda memutuskan untuk melepaskan bilangan bulat, membuatnya lebih besar, lebih kecil, atau menambahkan anggota, akan ada ketidakcocokan antara jumlah alokasi kode memori pengguna, dan offset yang diharapkan kode konstruktor. Kemungkinan akibatnya adalah crash, jika Anda beruntung - jika Anda kurang beruntung, perangkat lunak Anda berperilaku aneh.
Dengan pimpl'ing, Anda dapat dengan aman menambah dan menghapus anggota data ke kelas dalam, karena alokasi memori dan panggilan konstruktor terjadi di pustaka bersama:
Yang perlu Anda lakukan sekarang adalah menjaga antarmuka publik Anda bebas dari anggota data selain penunjuk ke objek implementasi, dan Anda aman dari kelas kesalahan ini.
Sunting: Saya mungkin harus menambahkan bahwa satu-satunya alasan saya berbicara tentang konstruktor di sini adalah karena saya tidak ingin memberikan lebih banyak kode - argumen yang sama berlaku untuk semua fungsi yang mengakses anggota data.
sumber
class A_impl *impl_;
Kita tidak boleh lupa bahwa warisan lebih kuat, lebih erat daripada pendelegasian. Saya juga akan mempertimbangkan semua masalah yang diangkat dalam jawaban yang diberikan saat memutuskan idiom desain apa yang akan digunakan dalam memecahkan masalah tertentu.
sumber
Meskipun secara luas tercakup dalam jawaban lain mungkin saya bisa sedikit lebih eksplisit tentang satu manfaat pimpl daripada kelas basis virtual:
Pendekatan pimpl transparan dari sudut pandang pengguna, artinya Anda dapat, misalnya, membuat objek kelas pada tumpukan dan menggunakannya secara langsung dalam wadah. Jika Anda mencoba menyembunyikan implementasi menggunakan kelas dasar virtual abstrak, Anda perlu mengembalikan penunjuk bersama ke kelas dasar dari pabrik, yang memperumit penggunaannya. Pertimbangkan kode klien yang setara berikut ini:
sumber
Dalam pemahaman saya, kedua hal ini memiliki tujuan yang sangat berbeda. Tujuan dari idiom jerawat pada dasarnya adalah memberi Anda pegangan untuk penerapan Anda sehingga Anda dapat melakukan hal-hal seperti pertukaran cepat untuk semacamnya.
Tujuan dari kelas virtual lebih pada garis memungkinkan polimorfisme, yaitu Anda memiliki pointer yang tidak diketahui ke objek dari tipe turunan dan ketika Anda memanggil fungsi x Anda selalu mendapatkan fungsi yang tepat untuk kelas apa pun yang sebenarnya ditunjuk oleh pointer dasar.
Apel dan jeruk banget.
sumber
Masalah paling menjengkelkan tentang idiom pimpl adalah membuatnya sangat sulit untuk memelihara dan menganalisis kode yang ada. Jadi menggunakan pimpl Anda membayar dengan waktu pengembang dan frustrasi hanya untuk "mengurangi ketergantungan dan waktu build dan meminimalkan eksposur header dari detail implementasi". Putuskan sendiri, apakah itu sangat berharga.
Terutama "waktu pembuatan" adalah masalah yang dapat Anda selesaikan dengan perangkat keras yang lebih baik atau menggunakan alat seperti Incredibuild (www.incredibuild.com, juga sudah termasuk dalam Visual Studio 2017), sehingga tidak memengaruhi desain perangkat lunak Anda. Desain perangkat lunak umumnya harus independen dari cara perangkat lunak dibuat.
sumber