klassen dynamisch erstellen
-
ich möchte dynamisch instanzen von klassen erzeugen.
die klassen stehen in einem arraynumOfNewClasses = 2; const char newClass[] = {"newClass1","newClass2"};
um das "dynamisch machen zu können habe ich ein define angelegt:
#define ADDCLASS(ClassName) \ { \ ClassName *ClassName = new ClassName; \ }
nun habe ich folgendes versucht:
for (int i=0; i<numOfNewClasses; i++) { ADDCLASS(newClass[i]); }
allerdings haut das alles nicht so ganz hin wie geplant.
jemand nen tip, wie ich das machen könnte?!
-
hatte auch schon über ne map nachgedacht
so in etwa:for (int i=0; i<numOfNewClasses; i++) { mymap.insert(newClass[i], new newclass[i]); }
aber das wird so auch nicht gehen
-
Wenn du eine gemeinsame Basisklasse hast, kannst du alles in einen std::deque<Basisklasse*> reinstecken.
-
#include <map> #include <string> class IObject { public: virtual void foo() = 0 { } virtual ~IObject() { } }; class e_cant_find_class_creator { }; class ClassFactory { public: typedef IObject * (*PFCreator)(); public: void RegisterObjectMaker(const char *className, PFCreator creator) { creators[className] = creator; } IObject * Create(const char *className) { PFCreator creator = creators[className]; if(creator) { return creator(); } throw e_cant_find_class_creator(); } private: std::map<std::string, PFCreator> creators; }; class A : public IObject { virtual void foo() { std::cout << "instance of class A\n"; } public: static IObject * CreateInstance() { return new A; } }; class B : public IObject { virtual void foo() { std::cout << "instance of class B\n"; } public: static IObject * CreateInstance() { return new B; } }; int main() { ClassFactory factory; factory.RegisterObjectMaker("newClass1", &A::CreateInstance); factory.RegisterObjectMaker("newClass2", &B::CreateInstance); const char *newClass[] = {"newClass1", "newClass2", "newClass2", "newClass1", "newClass1"}; size_t numOfNewClasses = sizeof(newClass) / sizeof(newClass[0]); for(size_t i = 0; i < numOfNewClasses; ++i) { std::auto_ptr<IObject> pObj(factory.Create(newClass[i])); pObj->foo(); } }
-
EDIT: "ssm" war schneller!
asr schrieb:
aber das wird so auch nicht gehen
Leider werden in C++ Klassennamen zur Compile-Zeit aufgeloest. D.h. Du kannst nicht einfach einen String nehmen als Name fuer eine Klasse (theoretisch wuerde das schon gehen, aber der C++ Standard sieht das nicht vor).
Die Haeufigste Loesung dafuer ist eine Factory:
#include <string> using std::string; class BaseClass { // ... }; class Alpha : public BaseClass { // ... }; class Beta : public BaseClass { // ... }; class MyClassFactory { public: static BaseClass* Create( const string& name ); }; BaseClass* MyClassFactory::Create( const string& name ) { if ( name == "Alpha" ) return new Alpha(); if ( name == "Beta" ) return new Beta(); return 0; // unbekannte Klasse } void func( void ) { Alpha* a = dynamic_cast<Alpha*>( MyClassFactory::Create( "Alpha" ); Beta* b = dynamic_cast<Beta*>( MyClassFactory::Create( "Beta" ); }
-
Power Off schrieb:
class MyClassFactory {
public:
static BaseClass* Create( const string& name );
};wozu die klasse, wenn sie nur eine static-methode hat?
also einfacher
BaseClass* myClassFactoryCreate( const string& name ) { if ( name == "Alpha" ) return new Alpha(); if ( name == "Beta" ) return new Beta(); return 0; // unbekannte Klasse }
und ein bißchen weniger brimborium.
Leider werden in C++ Klassennamen zur Compile-Zeit aufgeloest. D.h. Du kannst nicht einfach einen String nehmen als Name fuer eine Klasse
gut erkannt. aber in deinen code können wir fein ein makro einbauen, damit endlich eins drin ist.
#define ADDCLASS(class) if(name==#class) return new class; BaseClass* myClassFactoryCreate( const string& name ) { ADDCLASS(Alpha); ADDCLASS(Beta); return 0; // unbekannte Klasse }
-
volkard schrieb:
wozu die klasse, wenn sie nur eine static-methode hat?
Oh, volkard. Das ist doch nur ein Beispiel. In der Praxis will man vielleicht viel mehr da reinpacken.
Ausserdem kann man so alles Factory-Maessige in dieselbe Klasse packen.
volkard schrieb:
#define ADDCLASS(class) if(name==#class) return new class; BaseClass* myClassFactoryCreate( const string& name ) { ADDCLASS(Alpha); ADDCLASS(Beta); return 0; // unbekannte Klasse }
Stimmt, kann man auch so machen, aber besser noch waere:
#define ADDCLASS(class) if ( name == #class ) return new class BaseClass* myClassFactoryCreate( const string& name ) { ADDCLASS(Alpha)(); ADDCLASS(Beta)(); return 0; // unbekannte Klasse } #undef ADDCLASS
Falls man noch Parameter an den jeweiligen Konstruktor uebergeben will.
-
Power Off schrieb:
volkard schrieb:
wozu die klasse, wenn sie nur eine static-methode hat?
Oh, volkard. Das ist doch nur ein Beispiel. In der Praxis will man vielleicht viel mehr da reinpacken.
Ausserdem kann man so alles Factory-Maessige in dieselbe Klasse packen.
volkard schrieb:
#define ADDCLASS(class) if(name==#class) return new class; BaseClass* myClassFactoryCreate( const string& name ) { ADDCLASS(Alpha); ADDCLASS(Beta); return 0; // unbekannte Klasse }
Stimmt, kann man auch so machen, aber besser noch waere:
#define ADDCLASS(class) if ( name == #class ) return new class BaseClass* myClassFactoryCreate( const string& name ) { ADDCLASS(Alpha)(); ADDCLASS(Beta)(); return 0; // unbekannte Klasse } #undef ADDCLASS
Falls man noch Parameter an den jeweiligen Konstruktor uebergeben will.
bei mir war im makro ein semikolon zu viel.
im code dasADDCLASS(Alpha);
war gut.
und deinADDCLASS(Alpha)();
ist nicht so gut, weil die klammern da nutzlos und unüblich sind.
aber kannst natürlich malADDCLASS(Alpha)(eingabedatei);
schreiben.
-
volkard schrieb:
ADDCLASS(Alpha)();
ist nicht so gut, weil die klammern da nutzlos und unüblich sind.
Das ist nicht ganz korrekt. Einige Compiler bringen eine Fehlermeldung, wenn man bei explizit deklarierten Konstruktoren die Klammern weglaesst.
Hab grad im Standard nachgeguckt, aber nix gefunden, was das Weglassen des new-initializers als Fehler, fehlgeformt oder korrekt betrachten wuerde. Der Standard scheint fuer den Fall keine Vorschrift zu haben.
-
danke erstmal für eure antworten.
nur glaube ich, das es in meinem fall so auch nicht einfacher wird.
(vielleicht hab ich euro lösung aber auch falsch durchblickt.)was ich nun versuche ist folgendes:
ich versuche eine map anzulegen in in der immer den namen klasse
und einen pointer darauf zu speichern.std::map<const char*,void*> myClasses; myClassA *pMyClassA = new MyClassA();
beim hinzufügen jedoch:
myClasses.insert("class A", pMyClassA);
bekomme ich folgende fehlermeldung:
x:/xxx.cpp(638): error C2664: 'std::_Tree<_Traits>::iterator std::_Tree<_Traits>::insert(std::_Tree<_Traits>::iterator,const std::_Tree<_Traits>::value_type &)' : cannot convert parameter 1 from 'const char [18]' to 'std::_Tree<_Traits>::iterator' with [ _Traits=std::_Tmap_traits<const char *,void *,std::less<const char *>,std::allocator<std::pair<const char *const ,void *>>,false> ] and [ _Traits=std::_Tmap_traits<const char *,void *,std::less<const char *>,std::allocator<std::pair<const char *const ,void *>>,false> ]
-
std::map<const char*,void*> myClasses; myClasses["ClassA"] = new ClassA;
haut leider immer noch nicht so ganz hin
ich kann zwar mit obigen code in die map hineinschreibenallerdings hatte ich gehofft nun auch auf methoden von ClassA
mittelsmyClasses["ClassA"]->methode1();
zugreifen zu können.
noch jemand nen tip hierzu?
-
Power Off schrieb:
Das ist nicht ganz korrekt. Einige Compiler bringen eine Fehlermeldung, wenn man bei explizit deklarierten Konstruktoren die Klammern weglaesst.
erstens gleib ich das nicht und zweitens wäre so ein compiler kaputt und man muß nicht jeden bug pflegen.
-
@asr
tatsache ist: du kannst in c++ klassen nicht dynamisch erzeugen, sondern nur objekte. mit deinen new-aufrufen erzeugst du nie eine klasse sondern immer nur ein objekt vom typ dieser klasse.wenn du uns schreiben würdest, welches problem du mit "dynamischen" klassen lösen willst, dann fände sich bestimmt eine alternative dazu.
-
std::map<const char*,void*> myClasses;
Mach mal lieber aus dem "char*" ein "string" und das const kann dann auch weg, weil die map sich das eh kopiert
-
Nicht
myClasses.insert("class A", pMyClassA);
sondern
myClasses.insert( std::make_pair("class A", pMyClassA) );
-
Konfusius schrieb:
@asr
tatsache ist: du kannst in c++ klassen nicht dynamisch erzeugen, sondern nur objekte. mit deinen new-aufrufen erzeugst du nie eine klasse sondern immer nur ein objekt vom typ dieser klasse.hatte ich schon so gemeint
Pellaeon schrieb:
std::map<string,void*> myClasses;
gut, hab ich geändert. machts aber weder besser noch schlechter
kartoffelsack schrieb:
myClasses.insert( std::make_pair("class A", pMyClassA) );
ändert auch nichts. was soll das genau bewirken?
Konfusius schrieb:
wenn du uns schreiben würdest, welches problem du mit "dynamischen" klassen lösen willst, dann fände sich bestimmt eine alternative dazu.
gut, was ich machen wollte/will ist folgendes.
ich habe ein programm, aus dem man aus einem auswahlmenü funktionen anderer klassen aufrufen kann. diese klassen sind immer ähnlich aufgebaut.
und um das menü, etc nicht immer von hand erweitern zu müssen, dachte ich dass ich einfach ein array mache,
in dem ich die klassennamen speicher.also in etwa dieses:
numOfNewClasses = 2; const char newClass[] = {"newClass1","newClass2"};
sinn des ganzen sollte es sein, dass wenn eine klasse hinzukommt, an möglichst wenig
stellen der code geändert werden muss.deswegen dachte ich erst daran das mit einem #define zu lösen.
scheint aber nicht machbar zu sein.
deswegen die idee mit der map. die taugt mir im prinzip auch erstmal.
nur kann ich leider nicht über die pointer auf je so ein objekt meiner klassenauf die methoden meiner klassen zugreifen.
das war das was ich mit
myClasses["ClassA"]->methode1();
machen wollte.
-
dein wert in der Map ist "void*" und einen Void-Zeiger kann man nicht dereferenzieren, den muss man vorher casten.
-
Pellaeon schrieb:
dein wert in der Map ist "void*" und einen Void-Zeiger kann man nicht dereferenzieren, den muss man vorher casten.
stimmt!
so gehts
((ClassA*)myClasses["ClassA"])->methode1();
aber dann tu ich mir ja später noch schwerer das "dynamisch" zu gestalten
-
Heißen die Mehoden denn immer gleich oder gibts da Unterschiede?
-
ich kann sie auch gleich benennen.
allerdings werden es nicht immer genau gleich viele sein.also wird es was wie
doThis1(); doThis2(); doThis2();
in jeder klasse geben. nur eben mal 3 davon mal mehr oder weniger