Sering kali itu ide yang baik untuk memiliki kelas dasar abstrak untuk mengisolasi antarmuka objek.
Masalahnya adalah bahwa konstruksi salinan, IMHO, cukup banyak rusak secara default di C ++, dengan copy constructor yang dihasilkan secara default.
Jadi, apa sajakah Gotcha ketika Anda memiliki kelas dasar abstrak dan petunjuk mentah di kelas turunan?
class IAbstract
{
~IAbstract() = 0;
}
class Derived : public IAbstract
{
char *theProblem;
...
}
IAbstract *a1 = new Derived();
IAbstract a2 = *a1;//???
Dan sekarang apakah Anda dengan bersih menonaktifkan konstruksi salinan untuk seluruh hierarki? Mengumumkan konstruksi salinan sebagai pribadi di IAbstract
?
Apakah ada aturan tiga dengan kelas dasar abstrak?
c++
abstract-class
Coder
sumber
sumber
Jawaban:
Salinan konstruksi pada kelas abstrak harus dibuat pribadi dalam banyak kasus, serta operator penugasan.
Kelas abstrak, menurut definisi, dibuat menjadi tipe polimorfik. Jadi, Anda tidak tahu berapa banyak memori yang digunakan oleh instance Anda, sehingga tidak dapat menyalin atau menetapkannya dengan aman. Dalam praktiknya, Anda berisiko mengiris: /programming/274626/what-is-the-slicing-problem-in-c
Jenis polimorfik, dalam C ++, tidak boleh dimanipulasi oleh nilai. Anda memanipulasi mereka dengan referensi atau dengan pointer (atau smart pointer).
Ini adalah alasan mengapa Java membuat objek dapat dimanipulasi hanya dengan referensi, dan mengapa C # dan D memiliki pemisahan antara kelas dan struct (yang pertama adalah tipe polimorfik dan referensi, yang kedua adalah non polimorfik dan tipe nilai).
sumber
Anda bisa, tentu saja, membuatnya terlindungi dan kosong, sehingga kelas turunan dapat memilih. Namun, lebih umum, kode Anda dilarang pula karena tidak mungkin untuk instantiate
IAbstract
- karena memiliki fungsi virtual murni. Dengan demikian, ini umumnya bukan masalah - kelas antarmuka Anda tidak dapat dipakai dan oleh karena itu tidak dapat disalin, dan kelas Anda yang lebih diturunkan dapat melarang atau terus menyalin seperti yang mereka inginkan.sumber
operator=(const Derived2&)
diDerived1
.Dengan membuat ctor dan tugas pribadi (atau dengan mendeklarasikannya sebagai = delete di C ++ 11) Anda menonaktifkan salinan.
Intinya di sini adalah DI MANA Anda harus melakukan itu. Untuk tetap menggunakan kode Anda, IAbstract tidak menjadi masalah. (perhatikan bahwa melakukan apa yang Anda lakukan, Anda menetapkan
*a1
IAbstract
subobjek ke a2, kehilangan referensi apa punDerived
. Penugasan nilai bukan polimorfik)Masalahnya datang dengan
Derived::theproblem
. Menyalin Turunan ke yang lain mungkin sebenarnya berbagi*theproblem
data yang mungkin tidak dirancang untuk dibagikan (ada dua contoh yang dapat memanggildelete theproblem
destruktor mereka).Jika itu masalahnya, itu
Derived
harus tidak dapat disalin dan tidak dapat dialihkan. Tentu saja, jika Anda membuat salinan pribadi dalamIAbstract
, karena salinan default untukDerived
kebutuhan itu,Derived
juga tidak akan dapat disalin. Tetapi jika Anda mendefinisikan sendiriDerived::Derived(const Derived&)
tanpa memanggilIAbtract
salinan, Anda masih dapat menyalinnya.Masalahnya adalah dalam Derived, dan solusinya harus tetap ke Derived: jika itu harus objek dinamis yang hanya diakses oleh pointer atau referensi, itu adalah Derived itu sendiri yang harus memiliki
Pada dasarnya tergantung pada perancang kelas Derived (yang seharusnya tahu bagaimana Derived bekerja dan bagaimana
theproblem
dikelola) untuk memutuskan apa yang harus dilakukan dengan penugasan dan salinan.sumber