klassen dynamisch erstellen



  • ich möchte dynamisch instanzen von klassen erzeugen.
    die klassen stehen in einem array

    numOfNewClasses			= 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 das

    ADDCLASS(Alpha);
    

    war gut.
    und dein

    ADDCLASS(Alpha)();
    

    ist nicht so gut, weil die klammern da nutzlos und unüblich sind.
    aber kannst natürlich mal

    ADDCLASS(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 hineinschreiben

    allerdings hatte ich gehofft nun auch auf methoden von ClassA
    mittels

    myClasses["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 klassen

    auf 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


Anmelden zum Antworten