Objekte per Benutzereingabe initialisieren



  • Also ich habe eine Basisklasse "Task", mehrere abgeleitete Klassen (im Sourcecode "Counter" und "Printer") und eine "Taskmanager" Klasse. Jetzt soll ein Benutzer eingeben können, welche Klasse(n) an den Taskmanager übergeben werden sollen. Ich hab das im Moment so gelöst:

    // Vereinfacht
    TaskManager taskman;
    
    std::map<std::string, Task *> tasks;
    tasks["counter"] = new Counter;
    tasks["printer"] = new Printer;
    
    std::string objectname;
    std::cin >> objectname;
    
    taskman.AddTask(tasks[objectname]);
    
    while (taskman.NotFinished())
    {
        taskman.RunAllTasks();
        Sleep(1000);
    }
    
    delete tasks["counter"];
    delete tasks["printer"];
    

    Allerdings werden so erst einmal alle Objekte initialisiert, auch wenn nur wenige davon gebraucht werden. Da ich von den abgeleiteten Klassen an die 20 habe, ist das eine ziemliche Speicherverschwendung. Wie kann ich das ganze so lösen, dass ich nicht jede Klasse erst initialisieren muss ?



  • Bei einer map kannst du zur Laufzeit mittels insert einträge einfügen, wenn du das meinst.

    EDIT:
    Und du solltest dir mal Iteratoren anschauen. Das was du machst misshandelt ein wenig den Sinn eines Containers. 😉



  • Hallo,

    Initialisiere sie doch erstmal mit 0 und erzeuge deine Instanzen erst dann mit new, wenn du sie brauchst.



  • Im Prinzip hab ich ein ähnliches Problem wie dieses hier.

    Nur das ich keine Funktionen aufrufen will, sondern Objekte übergebe. Von Funktionen bekomme ich ja den Pointer ohne dass ich sie initialisiere oder ähnliches. Damit ich von den Objekten einen Pointer bekomme, muss ich sie ja erst initialisieren. Und da liegt mein Problem, da ich nicht viele Objekte initialisieren will, die ich später vielleicht gar nicht brauche.



  • Task *eval(std::string function, std::string argument)
    {
        Task *task;
        if (function == "counter")
        {
            task = new Counter(argument);
        }
        else if (function == "printer")
        {
            task = new Printer(argument);
        }
        return task;
    }
    

    Ich kann zwar die Objekte mit dieser Funktion erst dann erzeugen, wenn ich sie brauche, aber diese Variante ist so schlecht erweiterbar. Wenn ich zB. weitere Objekte aus einer DLL (oder ähnlichem) laden will, ist diese Variante ziemlich ungeschickt.



  • Such mal nach "abstract factory".


  • Administrator

    Und wie wäre es, wenn du einfach die Tipps befolgst, welche dir bereits im dem Thread gegeben wurden? Wie zum Beispiel den hier:

    Braunstein schrieb:

    Initialisiere sie doch erstmal mit 0 und erzeuge deine Instanzen erst dann mit new, wenn du sie brauchst.

    Einfach und effektiv. Kann man wenn man will sogar noch in eine eigene Klasse packen, welche die ganze Funktionalität übernimmt. Und über ein register_task, wobei man ein Task-Allocator übergibt und ein Name, könnte man das noch weiter verfeinern. Aber naja ...

    Und wenn du nur eine Instanz vom Counter, Printer usw. im ganzen Projekt haben möchtest, könntest du dir sogar noch Singleton anschauen. Wobei du dann die statische get_instance Funktion in der Map speichern könntest.

    typedef Task (*get_instance_fptr)();
    

    Grüssli



  • Braunstein schrieb:

    Initialisiere sie doch erstmal mit 0 und erzeuge deine Instanzen erst dann mit new, wenn du sie brauchst.

    Tut mir leid, wenn ich mich grad ziemlich dumm anstelle, aber ich weiß nicht genau, wie das gemeint ist.


  • Administrator

    Caster schrieb:

    Tut mir leid, wenn ich mich grad ziemlich dumm anstelle, aber ich weiß nicht genau, wie das gemeint ist.

    Kein Problem, man kann hier ja Fragen 😉

    Hier mal eine Möglichkeit. Kann man sicher aber auch noch anders lösen:

    class CAbstractTaskAdapter
    {
    public:
        virtual ~CAbstractTaskAdapter() { };
    
        virtual Task* get_task() = 0;
        virtual void clear_task() = 0;
    }
    
    template<typename T>
    class CTaskAdaptor : public CAbstractTaskAdapter
    {
        // Attributes //
    private:
        T* m_pTask;
    
        // Constructors & Destructor //
    public:
        CTaskAdaptor() : m_pTask(NULL) { };
        CTaskAdaptor(CTaskAdaptor const& rTaskAdaptor) : m_pTask(rTaskAdaptor.m_pTask) { };
    
        virtual ~CTaskAdaptor() { };
    
        // Operators //
    public:
        CTaskAdaptor& operator =(CTaskAdaptor const& rTaskAdaptor)
        {
            m_pTask = rTaskAdaptor.m_pTask;
            return *this;
        }
    
        // Methods //
    public:
        virtual Task* get_task()
        {
            if(!m_pTask)
            { m_pTask = new T(); }
    
            return m_pTask;
        }
    
        virtual void clear_task() { delete m_pTask; m_pTask = NULL; };
    };
    
    // Dein Codebeispiel umgewandelt:
    
    // Vereinfacht
    TaskManager taskman;
    
    typedef std::map<std::string, CAbstractTaskAdaptor*> t_TaskMap;
    
    t_TaskMap tasks;
    
    tasks["counter"] = new CTaskAdaptor<Counter>;
    tasks["printer"] = new CTaskAdaptor<Printer>;
    
    std::string objectname;
    std::cin >> objectname;
    
    // Man müsste natürlich hier zuerst noch überprüfen, ob es überhaupt etwas sinnvolles findet.
    taskman.AddTask(tasks[objectname]->get_task());
    
    while (taskman.NotFinished())
    {
        taskman.RunAllTasks();
        Sleep(1000);
    }
    
    // Könnte man auch anders lösen, aber ist ja nur ein Beispiel.
    t_TaskMap::iterator Iter = tasks.begin();
    t_TaskMap::iterator End = tasks.end();
    
    CAbstractTaskAdaptor* pATA = NULL;
    
    for(; Iter != End; ++Iter)
    {
        pATA = Iter->second;
    
        if(pATA)
        {
            pATA->clear_task();
            delete pATA;
        }
    }
    
    // Irgendwie in der Art.
    // Ist nur auf die schnelle und ungetestet
    

    Ich hoffe das hilft dir weiter.

    Grüssli



  • Perfekt, danke für die tolle Antwort. Das macht genau was ich brauch. Jetzt modifizier ich es noch ein bisschen und alles ist perfekt.


Anmelden zum Antworten