Apakah mungkin dalam C ++ untuk memiliki fungsi anggota yang keduanya static
dan virtual
? Rupanya, tidak ada cara langsung untuk melakukannya (static virtual member();
adalah kesalahan kompilasi), tetapi apakah ada setidaknya cara untuk mencapai efek yang sama?
YAITU:
struct Object
{
struct TypeInformation;
static virtual const TypeInformation &GetTypeInformation() const;
};
struct SomeObject : public Object
{
static virtual const TypeInformation &GetTypeInformation() const;
};
Masuk akal untuk menggunakan GetTypeInformation()
keduanya pada instance ( object->GetTypeInformation()
) dan pada kelas (SomeObject::GetTypeInformation()
), yang dapat berguna untuk perbandingan dan penting untuk template.
Satu-satunya cara yang dapat saya pikirkan adalah menulis dua fungsi / fungsi dan konstanta, per kelas, atau menggunakan makro.
Ada solusi lain?
const
dalam metode tanda tangan menandaithis
pointer implisit sebagai konstan dan tidak dapat diterapkan pada metode statis karena mereka tidak memiliki parameter implisit.Jawaban:
Tidak, tidak ada cara untuk melakukannya, karena apa yang akan terjadi ketika Anda menelepon
Object::GetTypeInformation()
? Itu tidak bisa tahu versi kelas turunan mana untuk memanggil karena tidak ada objek yang terkait dengannya.Anda harus menjadikannya fungsi virtual non-statis agar berfungsi dengan baik; jika Anda juga ingin dapat memanggil versi kelas turunan tertentu secara non-virtual tanpa instance objek, Anda juga harus menyediakan versi non-virtual statis redundan kedua.
sumber
Banyak yang mengatakan itu tidak mungkin, saya akan melangkah lebih jauh dan mengatakan itu tidak berarti.
Anggota statis adalah sesuatu yang tidak berhubungan dengan instance apa pun, hanya untuk kelas.
Anggota virtual adalah sesuatu yang tidak berhubungan langsung dengan kelas mana pun, hanya dengan sebuah instance.
Jadi anggota virtual statis akan menjadi sesuatu yang tidak berhubungan dengan instance atau kelas apa pun.
sumber
static virtual
metode, tetapi metodestatic
murnivirtual
sangat berarti dalam suatu antarmuka.static const string MyClassSillyAdditionalName
.Saya mengalami masalah ini beberapa hari yang lalu: Saya memiliki beberapa kelas yang penuh dengan metode statis tetapi saya ingin menggunakan metode pewarisan dan virtual dan mengurangi pengulangan kode. Solusi saya adalah:
Alih-alih menggunakan metode statis, gunakan singleton dengan metode virtual.
Dengan kata lain, setiap kelas harus berisi metode statis yang Anda panggil untuk mendapatkan pointer ke instance kelas yang dibagi bersama. Anda dapat menjadikan konstruktor yang sebenarnya privat atau terlindungi sehingga kode luar tidak dapat menyalahgunakannya dengan membuat instance tambahan.
Dalam praktiknya, menggunakan singleton sangat mirip dengan menggunakan metode statis kecuali bahwa Anda dapat mengambil keuntungan dari metode pewarisan dan virtual.
sumber
Itu mungkin!
Tapi apa sebenarnya yang mungkin, mari kita persempit. Orang sering menginginkan semacam "fungsi virtual statis" karena duplikasi kode yang diperlukan untuk dapat memanggil fungsi yang sama melalui panggilan statis "SomeDerivedClass :: myfunction ()" dan panggilan polimorf "base_class_pointer-> myfunction ()". Metode "Legal" untuk mengizinkan fungsi seperti itu adalah duplikasi definisi fungsi:
Bagaimana jika kelas dasar memiliki sejumlah besar fungsi statis dan kelas turunan harus menimpanya masing-masing dan orang lupa untuk memberikan definisi duplikat untuk fungsi virtual. Benar, kami akan mendapatkan beberapa kesalahan aneh saat runtime yang sulit dilacak. Menyebabkan duplikasi kode adalah hal yang buruk. Berikut ini mencoba untuk menyelesaikan masalah ini (dan saya ingin memberitahu sebelumnya bahwa itu benar-benar aman dan tidak mengandung ilmu hitam seperti typeid atau dynamic_cast :)
Jadi, kami ingin memberikan hanya satu definisi getTypeInformation () per kelas turunan dan jelas bahwa itu harus merupakan definisi dari statisberfungsi karena tidak mungkin untuk memanggil "SomeDerivedClass :: getTypeInformation ()" jika getTypeInformation () adalah virtual. Bagaimana kita bisa memanggil fungsi statis dari kelas turunan melalui pointer ke kelas dasar? Tidak mungkin dengan vtable karena vtable hanya menyimpan pointer ke fungsi virtual dan karena kami memutuskan untuk tidak menggunakan fungsi virtual, kami tidak dapat memodifikasi vtable untuk keuntungan kami. Kemudian, untuk dapat mengakses fungsi statis untuk kelas turunan melalui pointer ke kelas dasar kita harus menyimpan entah bagaimana jenis objek di dalam kelas dasarnya. Salah satu pendekatannya adalah membuat kelas dasar di-templatized menggunakan "pola templat berulang-ulang yang anehnya" tetapi tidak sesuai di sini dan kami akan menggunakan teknik yang disebut "type erasure":
Sekarang kita dapat menyimpan jenis objek dalam kelas dasar "Object" dengan variabel "keeper":
Dalam penjaga kelas turunan harus diinisialisasi selama konstruksi:
Mari kita tambahkan gula sintaksis:
Sekarang deklarasi keturunan terlihat seperti:
pemakaian:
Keuntungan:
Kekurangan:
Masalah terbuka:
1) ada berbagai nama untuk fungsi statis dan virtual cara mengatasi ambiguitas di sini?
2) bagaimana cara memanggil OVERRIDE_STATIC_FUNCTIONS secara implisit di dalam setiap konstruktor?
sumber
Sementara Alsk telah memberikan jawaban yang cukup terperinci, saya ingin menambahkan alternatif, karena saya pikir penerapannya yang disempurnakan terlalu rumit.
Kita mulai dengan kelas dasar abstrak, yang menyediakan antarmuka untuk semua jenis objek:
Sekarang kita membutuhkan implementasi yang sebenarnya. Tetapi untuk menghindari keharusan menulis metode statis dan virtual, kita akan meminta kelas objek aktual kita mewarisi metode virtual. Ini jelas hanya berfungsi, jika kelas dasar tahu bagaimana mengakses fungsi anggota statis. Jadi kita perlu menggunakan templat dan meneruskan nama kelas objek aktual ke sana:
Akhirnya kita perlu mengimplementasikan objek nyata kita. Di sini kita hanya perlu mengimplementasikan fungsi anggota statis, fungsi anggota virtual akan diwarisi dari kelas templat ObjectImpl, dipakai dengan nama kelas turunan, sehingga akan mengakses anggota statis itu.
Mari tambahkan beberapa kode untuk diuji:
Tambahan (12 Januari 2019):
Alih-alih menggunakan fungsi GetClassNameStatic (), Anda juga dapat mendefinisikan nama kelas sebagai anggota statis, bahkan "inline", yang bekerja IIRC sejak C ++ 11 (jangan takut oleh semua pengubah :)):
sumber
Itu mungkin. Buat dua fungsi: statis dan virtual
sumber
Tidak, ini tidak mungkin, karena fungsi anggota statis tidak memiliki
this
pointer. Dan anggota statis (baik fungsi dan variabel) tidak benar-benar anggota kelas per-se. Mereka kebetulan dipanggil olehClassName::member
, dan mematuhi penentu akses kelas. Penyimpanan mereka didefinisikan di suatu tempat di luar kelas; penyimpanan tidak dibuat setiap kali Anda membuat objek kelas. Pointer ke anggota kelas khusus dalam semantik dan sintaksis. Pointer ke anggota statis adalah pointer normal dalam semua hal.fungsi virtual di kelas membutuhkan
this
pointer, dan sangat digabungkan ke kelas, karenanya mereka tidak bisa statis.sumber
this
pointer. fungsi statis tidak spesifik untuk sebuah instance, dan tidak memerlukannya. Jadi - itu bukan alasan anggota statis virtual tidak mungkin.Yah, jawaban yang agak terlambat tetapi mungkin menggunakan pola templat berulang yang aneh. Ini wikipedia artikel memiliki info yang Anda butuhkan dan juga contoh di bawah statis polimorfisme adalah apa yang Anda minta.
sumber
Saya pikir apa yang Anda coba lakukan dapat dilakukan melalui template. Saya mencoba membaca yang tersirat di sini. Apa yang Anda coba lakukan adalah memanggil metode dari beberapa kode, di mana ia memanggil versi turunan tetapi pemanggil tidak menentukan kelas mana. Contoh:
Anda ingin Try () memanggil versi Bar M tanpa menentukan Bar. Cara Anda melakukannya untuk statika adalah dengan menggunakan templat. Jadi ubah seperti ini:
sumber
Tidak, fungsi anggota statis tidak boleh virtual. Karena konsep virtual diselesaikan pada saat dijalankan dengan bantuan vptr, dan vptr bukan anggota statis suatu kelas. Karena fungsi anggota statis tidak dapat mengakses vptr sehingga anggota statis dapat menjadi virtual.
sumber
Itu tidak mungkin, tapi itu hanya karena kelalaian. Itu bukan sesuatu yang "tidak masuk akal" karena banyak orang tampaknya mengklaim. Untuk lebih jelasnya, saya berbicara tentang sesuatu seperti ini:
Ini adalah 100% sesuatu yang dapat diimplementasikan (hanya saja belum), dan saya berpendapat sesuatu yang berguna.
Pertimbangkan cara kerja fungsi virtual normal. Hapus
static
s dan tambahkan beberapa hal lain dan kami punya:Ini berfungsi dengan baik dan pada dasarnya yang terjadi adalah kompiler membuat dua tabel, yang disebut VTables, dan memberikan indeks ke fungsi virtual seperti ini
Selanjutnya setiap kelas dengan fungsi virtual ditambah dengan bidang lain yang menunjuk ke VTable-nya, sehingga kompiler pada dasarnya mengubahnya menjadi seperti ini:
Lalu apa yang sebenarnya terjadi ketika Anda menelepon
b->sayMyName()
? Pada dasarnya ini:(Parameter pertama menjadi
this
.)Oke, jadi bagaimana cara kerjanya dengan fungsi virtual statis? Nah apa perbedaan antara fungsi anggota statis dan non-statis? Satu-satunya perbedaan adalah bahwa yang terakhir mendapatkan
this
pointer.Kita dapat melakukan hal yang persis sama dengan fungsi virtual statis - cukup hapus
this
pointer.Ini kemudian dapat mendukung kedua sintaks:
Jadi abaikan semua penentang. Ini tidak masuk akal. Mengapa itu tidak didukung? Saya pikir itu karena manfaatnya sangat kecil dan bahkan bisa sedikit membingungkan.
Satu-satunya keunggulan teknis atas fungsi virtual normal adalah Anda tidak perlu beralih
this
ke fungsi tersebut, tetapi saya tidak berpikir itu akan membuat perbedaan yang terukur untuk kinerja.Itu berarti Anda tidak memiliki fungsi statis dan non-statis yang terpisah untuk kasus ketika Anda memiliki instance, dan ketika Anda tidak memiliki instance, tetapi juga mungkin membingungkan bahwa itu hanya benar-benar "virtual" ketika Anda menggunakan panggilan instan.
sumber
Tidak, itu tidak mungkin, karena anggota statis terikat pada waktu kompilasi, sementara anggota virtual terikat pada saat runtime.
sumber
Pertama, balasannya benar bahwa yang diminta OP adalah kontradiksi dalam hal: metode virtual bergantung pada jenis run-time dari sebuah instance; fungsi statis secara khusus tidak bergantung pada instance - hanya pada tipe. Yang mengatakan, masuk akal untuk memiliki fungsi statis mengembalikan sesuatu yang spesifik untuk suatu tipe. Sebagai contoh, saya memiliki keluarga kelas MouseTool untuk pola Negara dan saya mulai memiliki masing-masing memiliki fungsi statis mengembalikan pengubah keyboard yang menyertainya; Saya menggunakan fungsi-fungsi statis di fungsi pabrik yang membuat instance MouseTool yang benar. Fungsi itu memeriksa keadaan mouse terhadap MouseToolA :: keyboardModifier (), MouseToolB :: keyboardModifier (), dll. Dan kemudian instantiated yang sesuai. Tentu saja kemudian saya ingin memeriksa apakah keadaannya benar jadi saya ingin menulis sesuatu seperti "
Jadi, jika Anda mendapati diri Anda menginginkan ini, Anda mungkin ingin mengulang solusi Anda. Namun, saya mengerti keinginan untuk memiliki metode statis dan kemudian menyebutnya secara dinamis berdasarkan pada jenis contoh yang dinamis. Saya pikir Pola Pengunjung dapat memberikan apa yang Anda inginkan. Ini memberi Anda apa yang Anda inginkan. Ini sedikit kode tambahan, tetapi bisa bermanfaat bagi pengunjung lain.
Lihat: http://en.wikipedia.org/wiki/Visitor_pattern untuk latar belakang.
Kemudian untuk setiap Objek konkret:
dan kemudian tentukan pengunjung dasar:
Kemudian pengunjung konkret yang memilih fungsi statis yang sesuai:
akhirnya, gunakan:
Catatan:
Jika Anda ingin menghindari kesalahan salin-tempel di mana salah satu metode kunjungan Anda memanggil fungsi statis yang salah, Anda bisa menggunakan fungsi pembantu templated (yang tidak bisa sendiri virtual) t pengunjung Anda dengan templat seperti ini:
sumber
Foo foo; ... foo::bar();
bukanFoo::bar();
). Itu tidak sepertidecltype(foo)::bar();
tetapi itu lagi akan terikat secara statis. Pendekatan pengunjung tampaknya merupakan cara yang masuk akal untuk mendapatkan perilaku ini tanpa hanya membuat metode statis menjadi metode const virtual.Dengan c ++ Anda dapat menggunakan pewarisan statis dengan metode crt. Sebagai contoh, ini digunakan secara luas pada templat jendela atl & wtl.
Lihat https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
Sederhananya, Anda memiliki kelas yang dibuat sendiri seperti kelas myclass: public myancestor. Dari titik ini kelas myancestor sekarang dapat memanggil fungsi T :: YourImpl statis Anda.
sumber
Mungkin Anda bisa mencoba solusi saya di bawah ini:
sumber
Base::mSelf
merujuk pada instance yang paling baru dibangun dari setiap kelas turunan, bahkan jika instance tersebut telah dihancurkan . jadiclass D1 : public Base ...; class D2 : public Base ...; ...; D1* pd1 = new D1(); D2* pd2 = new D2(); pd1->MyStaticFun(); /* calls D2::MyVirtualFun() */ delete pd2; pd1->MyStaticFun(); /* calls via deleted pd2 */
yang BUKAN apa yang diinginkan.Seperti yang dikatakan orang lain, ada 2 informasi penting:
this
pointer saat membuat panggilan fungsi statis danthis
pointer menunjuk ke struktur di mana tabel virtual, atau dunk, digunakan untuk mencari metode yang runtime untuk memanggil.Fungsi statis ditentukan pada waktu kompilasi.
Saya menunjukkan contoh kode ini di C + + anggota statis di kelas ; itu menunjukkan bahwa Anda dapat memanggil metode statis yang diberi pointer nol:
sumber
p < null
,p >= null
dll semua tidak terdefinisi juga.