Saya punya file: Base.h
class Base;
class DerivedA : public Base;
class DerivedB : public Base;
/*etc...*/
dan file lain: BaseFactory.h
#include "Base.h"
class BaseFactory
{
public:
BaseFactory(const string &sClassName){msClassName = sClassName;};
Base * Create()
{
if(msClassName == "DerivedA")
{
return new DerivedA();
}
else if(msClassName == "DerivedB")
{
return new DerivedB();
}
else if(/*etc...*/)
{
/*etc...*/
}
};
private:
string msClassName;
};
/*etc.*/
Apakah ada cara untuk mengubah string ini menjadi tipe aktual (kelas), sehingga BaseFactory tidak perlu mengetahui semua kelas Derived yang mungkin, dan memiliki if () untuk masing-masing dari mereka? Bisakah saya menghasilkan kelas dari string ini?
Saya pikir ini bisa dilakukan dalam C # melalui Refleksi. Apakah ada yang serupa di C ++?
c++
inheritance
factory
instantiation
Gal Goldman
sumber
sumber
Jawaban:
Tidak, tidak ada, kecuali Anda melakukan pemetaan sendiri. C ++ tidak memiliki mekanisme untuk membuat objek yang tipenya ditentukan saat runtime. Anda dapat menggunakan peta untuk melakukan pemetaan sendiri, meskipun:
Dan kemudian Anda bisa melakukannya
Mendapatkan contoh baru. Gagasan lain adalah meminta jenis mendaftarkan diri:
Anda dapat memutuskan untuk membuat makro untuk pendaftaran
Saya yakin ada nama yang lebih baik untuk keduanya. Hal lain yang mungkin masuk akal untuk digunakan di sini adalah
shared_ptr
.Jika Anda memiliki satu set tipe yang tidak terkait yang tidak memiliki kelas dasar yang sama, Anda dapat memberikan pointer fungsi tipe pengembalian
boost::variant<A, B, C, D, ...>
sebagai gantinya. Seperti jika Anda memiliki kelas Foo, Bar dan Baz, tampilannya seperti ini:A
boost::variant
seperti serikat pekerja. Ia tahu tipe mana yang disimpan di dalamnya dengan melihat objek apa yang digunakan untuk menginisialisasi atau menugaskannya. Lihat dokumentasinya di sini . Akhirnya, penggunaan pointer fungsi mentah juga agak oldish. Kode C ++ modern harus dipisahkan dari fungsi / tipe tertentu. Anda mungkin ingin melihat ke dalamBoost.Function
untuk mencari cara yang lebih baik. Maka akan terlihat seperti ini (peta):std::function
akan tersedia di versi C ++ selanjutnya, termasukstd::shared_ptr
.sumber
DerivedB::reg
sebenarnya diinisialisasi? Pemahaman saya adalah bahwa itu tidak dapat dibangun sama sekali jika tidak ada fungsi atau objek yang didefinisikan dalam unit terjemahanderivedb.cpp
, sesuai 3.6.2.BaseFactory::map_type * BaseFactory::map = NULL;
di dalam file cpp saya. Tanpa ini, linker mengeluh tentang peta simbol yang tidak dikenal.DerivedB::reg
tidak diinisialisasi jika tidak ada fungsi atau instansnya yang didefinisikan dalam unit terjemahanderivedb.cpp
. Itu berarti bahwa kelas tidak terdaftar sampai itu benar-benar instan. Adakah yang tahu solusi untuk itu?Tidak ada. Solusi pilihan saya untuk masalah ini adalah membuat kamus yang memetakan nama untuk metode pembuatan. Kelas-kelas yang ingin dibuat seperti ini kemudian mendaftarkan metode pembuatan dengan kamus. Ini dibahas secara rinci dalam buku pola GoF .
sumber
Jawaban singkatnya adalah Anda tidak bisa. Lihat pertanyaan SO ini untuk alasannya:
sumber
Saya telah menjawab pertanyaan SO lain tentang pabrik C ++. Silakan lihat di sana jika pabrik yang fleksibel menarik. Saya mencoba menggambarkan cara lama dari ET ++ untuk menggunakan macro yang telah bekerja sangat baik untuk saya.
ET ++ adalah proyek untuk port MacApp lama ke C ++ dan X11. Dalam upaya itu Eric Gamma dll mulai berpikir tentang Pola Desain
sumber
boost :: functional memiliki templat pabrik yang cukup fleksibel: http://www.boost.org/doc/libs/1_54_0/libs/functional/factory/doc/html/index.html
Namun preferensi saya adalah untuk menghasilkan kelas pembungkus yang menyembunyikan mekanisme pemetaan dan pembuatan objek. Skenario umum yang saya temui adalah kebutuhan untuk memetakan kelas turunan berbeda dari beberapa kelas dasar ke kunci, di mana kelas turunan semua memiliki tanda tangan konstruktor umum yang tersedia. Inilah solusi yang saya buat sejauh ini.
Saya umumnya menentang penggunaan makro yang berat, tapi saya membuat pengecualian di sini. Kode di atas menghasilkan GENERIC_FACTORY_MAX_ARITY + 1 versi kelas bernama GenericFactory_N, untuk setiap N antara 0 dan GENERIC_FACTORY_MAX_ARITY inklusif.
Menggunakan templat kelas yang dihasilkan mudah. Misalkan Anda ingin pabrik membuat objek turunan BaseClass menggunakan pemetaan string. Setiap objek yang diturunkan mengambil 3 bilangan bulat sebagai parameter konstruktor.
Destructor kelas GenericFactory_N adalah virtual untuk memungkinkan yang berikut ini.
Perhatikan bahwa baris ini makro generator pabrik generik
Mengasumsikan file header pabrik generik bernama GenericFactory.hpp
sumber
Solusi detail untuk mendaftarkan objek, dan mengaksesnya dengan nama string.
common.h
:test1.h
:main.cpp
:Kompilasi dan Jalankan (Telah melakukan ini dengan Eclipse)
Keluaran:
sumber
Arti refleksi seperti di Jawa. ada beberapa info di sini: http://msdn.microsoft.com/en-us/library/y0114hz2(VS.80).aspx
Secara umum, cari google untuk "c ++ reflection"
sumber
Tor Brede Vekterli menyediakan ekstensi boost yang memberikan fungsionalitas yang Anda cari. Saat ini, itu agak canggung dengan lib boost saat ini, tapi saya bisa membuatnya bekerja dengan 1.48_0 setelah mengubah namespace dasarnya.
http://arcticinteractive.com/static/boost/libs/factory/doc/html/factory/factory.html#factory.factory.reference
Sebagai jawaban bagi mereka yang mempertanyakan mengapa hal seperti itu (sebagai refleksi) akan berguna untuk c ++ - Saya menggunakannya untuk interaksi antara UI dan mesin - pengguna memilih opsi di UI, dan mesin mengambil string pemilihan UI, dan menghasilkan objek dari tipe yang diinginkan.
Manfaat utama menggunakan kerangka kerja di sini (lebih dari mempertahankan daftar buah di suatu tempat) adalah bahwa fungsi pendaftaran ada dalam definisi masing-masing kelas (dan hanya memerlukan satu baris kode yang memanggil fungsi pendaftaran per kelas terdaftar) - sebagai lawan dari file yang mengandung daftar buah, yang harus ditambahkan secara manual ke setiap kali kelas baru diturunkan.
Saya menjadikan pabrik sebagai anggota statis kelas dasar saya.
sumber
Ini adalah pola pabrik. Lihat wikipedia (dan contoh ini ). Anda tidak dapat membuat tipe per se dari string tanpa beberapa hack mengerikan. Mengapa Anda membutuhkan ini?
sumber