Mam plik: Base.h
class Base;
class DerivedA : public Base;
class DerivedB : public Base;
/*etc...*/
i inny plik: 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.*/
Czy istnieje sposób, aby w jakiś sposób przekonwertować ten ciąg na rzeczywisty typ (klasę), tak aby BaseFactory nie musiał znać wszystkich możliwych klas pochodnych i mieć if () dla każdej z nich? Czy mogę utworzyć klasę z tego ciągu?
Myślę, że można to zrobić w C # za pomocą refleksji. Czy jest coś podobnego w C ++?
c++
inheritance
factory
instantiation
Gal Goldman
źródło
źródło
Odpowiedzi:
Nie, nie ma, chyba że sam wykonasz mapowanie. C ++ nie ma mechanizmu do tworzenia obiektów, których typy są określane w czasie wykonywania. Możesz jednak użyć mapy, aby wykonać to mapowanie samodzielnie:
A potem możesz to zrobić
Zdobycie nowej instancji. Innym pomysłem jest zarejestrowanie się typów:
Możesz zdecydować się na utworzenie makra do rejestracji
Jestem pewien, że dla tych dwóch są lepsze nazwy. Inną rzeczą, która prawdopodobnie ma sens tutaj, jest
shared_ptr
.Jeśli masz zestaw niepowiązanych typów, które nie mają wspólnej klasy bazowej, możesz
boost::variant<A, B, C, D, ...>
zamiast tego nadać wskaźnikowi funkcji zwracany typ . Jak jeśli masz klasę Foo, Bar i Baz, wygląda to tak:A
boost::variant
jest jak związek. Wie, jaki typ jest w nim przechowywany, sprawdzając, jaki obiekt został użyty do zainicjowania lub przypisania do niego. Zajrzyj do jego dokumentacji tutaj . Wreszcie użycie surowego wskaźnika funkcji jest również nieco przestarzałe. Nowoczesny kod C ++ powinien być oddzielony od określonych funkcji / typów. Możesz zajrzeć,Boost.Function
aby znaleźć lepszy sposób. Wtedy wyglądałoby to tak (mapa):std::function
będzie również dostępny w następnej wersji C ++, w tymstd::shared_ptr
.źródło
DerivedB::reg
jest faktycznie zainicjowany? Rozumiem, że nie można go w ogóle skonstruować, jeśli w jednostce tłumaczeniowej nie zdefiniowano żadnej funkcji ani przedmiotuderivedb.cpp
, zgodnie z 3.6.2.BaseFactory::map_type * BaseFactory::map = NULL;
w moim pliku cpp. Bez tego linker narzekał na nieznaną mapę symboli.DerivedB::reg
nie jest inicjowany, jeśli żadna z jego funkcji lub instancji nie jest zdefiniowana w jednostce tłumaczeniowejderivedb.cpp
. Oznacza to, że klasa nie jest rejestrowana, dopóki nie zostanie faktycznie utworzona. Czy ktoś zna obejście tego problemu?Nie, nie ma. Moim preferowanym rozwiązaniem tego problemu jest utworzenie słownika, który odwzorowuje nazwę na sposób tworzenia. Klasy, które chcą zostać utworzone w ten sposób, rejestrują metodę tworzenia w słowniku. Jest to omówione szczegółowo w książce wzorców GoF .
źródło
Krótka odpowiedź brzmi: nie możesz. Zobacz te pytania SO, aby dowiedzieć się, dlaczego:
źródło
Odpowiedziałem w innym pytaniu SO dotyczącym fabryk C ++. Proszę, zobacz tam czy interesuje Cię elastyczna fabryka. Próbuję opisać stary sposób z ET ++ do używania makr, który świetnie się sprawdził.
ET ++ był projektem przeniesienia starego MacApp na C ++ i X11. W tym celu Eric Gamma itp. Zaczął myśleć o wzorcach projektowych
źródło
boost :: function ma dość elastyczny szablon fabryki: http://www.boost.org/doc/libs/1_54_0/libs/functional/factory/doc/html/index.html
Preferuję jednak generowanie klas opakowujących, które ukrywają mechanizm mapowania i tworzenia obiektów. Typowym scenariuszem, z którym się spotykam, jest potrzeba mapowania różnych klas pochodnych niektórych klas bazowych na klucze, gdzie wszystkie klasy pochodne mają dostępną wspólną sygnaturę konstruktora. Oto rozwiązanie, które do tej pory wymyśliłem.
Generalnie sprzeciwiam się intensywnemu używaniu makr, ale zrobiłem tutaj wyjątek. Powyższy kod generuje wersje GENERIC_FACTORY_MAX_ARITY + 1 klasy o nazwie GenericFactory_N, dla każdego N od 0 do GENERIC_FACTORY_MAX_ARITY włącznie.
Korzystanie z wygenerowanych szablonów klas jest łatwe. Załóżmy, że chcesz, aby fabryka tworzyła obiekty pochodne BaseClass przy użyciu mapowania ciągów. Każdy z obiektów pochodnych przyjmuje 3 liczby całkowite jako parametry konstruktora.
Destruktor klasy GenericFactory_N jest wirtualny, aby umożliwić następujące działania.
Zwróć uwagę, że ten wiersz ogólnego makra generatora fabrycznego
Zakłada się, że ogólny plik nagłówkowy fabryki nosi nazwę GenericFactory.hpp
źródło
Szczegółowe rozwiązanie do rejestracji obiektów i uzyskiwania dostępu do nich za pomocą nazw ciągów.
common.h
:test1.h
:main.cpp
:Skompiluj i uruchom (zrobiłem to z Eclipse)
Wynik:
źródło
Znaczenie odbicia jak w Javie. jest trochę informacji tutaj: http://msdn.microsoft.com/en-us/library/y0114hz2(VS.80).aspx
Ogólnie rzecz biorąc, wyszukaj w Google „odbicie c ++”
źródło
Tor Brede Vekterli zapewnia rozszerzenie doładowania, które zapewnia dokładnie taką funkcjonalność, jakiej szukasz. Obecnie jest to trochę niewygodne z obecnymi bibliotekami boost, ale udało mi się je uruchomić z 1.48_0 po zmianie jego podstawowej przestrzeni nazw.
http://arcticinteractive.com/static/boost/libs/factory/doc/html/factory/factory.html#factory.factory.reference
Odpowiadając tym, którzy pytają, dlaczego coś takiego (jak odbicie) miałoby się przydać w c ++ - używam go do interakcji między UI a silnikiem - użytkownik wybiera opcję w UI, a silnik bierze ciąg wyboru UI, i tworzy obiekt pożądanego typu.
Główną zaletą korzystania z tego frameworka w tym miejscu (zamiast utrzymywania gdzieś listy owoców) jest to, że funkcja rejestrująca znajduje się w definicji każdej klasy (i wymaga tylko jednej linii kodu wywołującej funkcję rejestracji na zarejestrowaną klasę) - w przeciwieństwie do pliku zawierającego lista owoców, którą należy dodać ręcznie za każdym razem, gdy tworzona jest nowa klasa.
Zrobiłem fabrykę statycznym członkiem mojej klasy bazowej.
źródło
To jest wzór fabryczny. Zobacz wikipedię (i ten przykład). Nie możesz stworzyć typu per se z łańcucha bez rażącego hackowania. Dlaczego tego potrzebujesz?
źródło