Objekt Container für den Globalen zugriff



  • Moin, bin gerade dabei mir zu überlegen wie ich eine globalen objectcontainer realisieren soll, damit ich von überall aus Zugriff auf die Daten haben kann.

    Zu meinem Projekt: Es besteht aus zwei Teilen, einem Core und einer GUI. Der Core bietet alle Klassen für die eigentliche Anwendung an und soll über eine GUI verwaltet werden. Dabei legt die GUI einzelne Objekte an und soll diese irgendwo zwischenlagern. Die Problematik ist nun, dass ich nicht weiß, wo ich die Pointer zu den Objekten lagern soll.

    Ich habe mir folgende Überlegungen schon gemacht:

    1. Ein Singleton eines speziellen Containers, der alle Objekte aufnehmen kann und auf anfrage diese wieder hergibt.
    #Pro
    - wäre vom Core unabhängig
    - Globaler Zugriff
    #Contra
    - Singleton
    - Objekte müssen gecastet werden

    2. Jede Klasse erbt von einer Superklasse die einen statischen Container und den Pointern.
    #Pro
    - Einfach zu realisieren
    #Contra
    - Eingriff in den Core
    - Muss ebenfalls gecastet werden

    // Container Klasse
    class A {
        public:
            A() {
                id_ = count_;
                pointer_.insert(pair<unsigned int,A*>(count_++,this));
            }
            virtual ~A() {
                pointer_.erase(id_);
            }
    
        private:
            unsigned int id_;
            static unsigned int count_;
            static map<unsigned int,A*> pointer_;
    };
    
    unsigned int A::count_ = 0;
    map<unsigned int, A*> A::pointer_;
    
    // meine Eigentlichen klassen
    class B : public A {
    
        public:
            B() : A() {}
    
    };
    

    Gäbe es sonst irgend welche attraktiven alternativen? 😕



  • Wieso brauchst du einen globalen Container für alle deine Objekte?

    Und gerade weil du Zeiger speicherst (erstmal egal ob Smart oder nicht), wann sollen die denn gelöscht werden?

    Und ein zweiter Punkt: Wie du merkst mit dem casten, ist der Ansatz schon zweifelhaft, weil fehleranfällig.



  • So sehe ich es auch.

    Es klingt so bequem, alles immer im Zugriff zu haben. Das ist der beste Weg zu einem chaotischen Design, wo alles immer von allem abhängt.

    Teile Dein Problem in Teile auf und die Teile kommunizieren über Parameter miteinander. Eine Klasse bekommt immer alle Daten, die es für seine Funktion brauch über eine definierte Schnittstelle. Häufig direkt im Konstruktor. So behält man die Übersicht über die Abhängigkeiten.

    Es gibt immer Ausnahmen, aber Ausnahmen haben die wichtige Eigenschaft, dass sie nicht die Regel sind und daher selten vorkommen sollten.

    Und noch was: vermeide Zeiger wo es nur geht. Rohe Zeiger sind ganz schlecht und smarte Zeiger wie std::unique_ptr sind auch nur die zweitbeste Lösung.



  • tntnet schrieb:

    Es klingt so bequem, alles immer im Zugriff zu haben. Das ist der beste Weg zu einem chaotischen Design, wo alles immer von allem abhängt.

    Nunja, ich wollte verhindern, dass meine Klasse von der Existenz des Containers wissen und so die Abhängigkeiten reduzieren. Aber das scheint wohl keine gute Idee gewesen zu sein.

    tntnet schrieb:

    Teile Dein Problem in Teile auf und die Teile kommunizieren über Parameter miteinander. Eine Klasse bekommt immer alle Daten, die es für seine Funktion brauch über eine definierte Schnittstelle. Häufig direkt im Konstruktor. So behält man die Übersicht über die Abhängigkeiten.

    #include <vector>
    #include <iostream>
    #include <algorithm>
    
    class Base;
    
    class Container {
    
        public:
            Container(){}
            ~Container(){}
    
            void add(Base* obj) {
                container_.push_back(obj);
            }
    
            void remove(Base* obj) {
                container_.erase(std::remove(container_.begin(),container_.end(),obj),container_.end());
            }   
    
            std::vector<Base*> container_;
    
    };
    
    class Base {
        public:
            Base(Container* cont) : container_(cont) {
                container_->add(this);
            }
            virtual ~Base() {
                container_->remove(this);
            }
        private:
            Container* container_;
    };
    
    class Derived : public Base {
        public:
            Derived(Container* cont) : Base(cont) {};
            ~Derived(){};
    
    };
    
    int main(int a, char** b) {
    
        Container* cont = new Container;
        Derived* der = new Derived(cont);
    
        //code ...
    
        delete der;
        delete cont;
    
        return 0;
    }
    

    Hab ich das nun richtig verstanden? Also mein Code hier ist stark vereinfacht und muss noch erweitert und auf Smartpointer umgeschrieben werden.

    So entledige ich mich dem Problem der Globalen Variable.
    Damit wissen alle Klassen, die im Container landen sollen, von der Existenz des Containers aber nicht umgekehrt.

    Ich komme nun aber nicht um das casten der Objekte herum, außer ich verwende unterschiedliche Container für verschiedene Klasse.



  • Das hast Du nicht richtig verstanden. Es geht darum, dass es falsch ist, so einen Objektcontainer für den globalen Zugriff überhaupt haben zu wollen. Du brauchst ihn nicht. Das ist ein Designfehler.

    Und in der Implementierung gibt es so ein paar Dinge, die, na sagen wir mal "falsch" sind.

    Eine Klasse sollte keine public member Variablen haben, wie Container::container_. Du brauchst auch diesen Zugriff nicht.

    Statt std::vector solltest Du einen std::set verwenden. Ich sehe keinen Sinn in vector.

    Im Base fehlt der Copykonstruktor und Assignment.

    Der Konstruktor von Base und Derived sollten explicit sein.

    Warum erstellst Du die Objekte in main auf dem Heap mit new? Warum nicht einfach auf dem Stack? Dann brauchst Du keinen Zeiger mehr und kannst nicht vergessen, aufzuräumen.

    Rohe Zeiger sind fast immer ein Fehler. Smartpointer sind immer nur höchstens die zweitbeste Lösung. Besser sind Stackobjekte.



  • tntnet schrieb:

    Rohe Zeiger sind fast immer ein Fehler.

    ➡

    Rohe besitzende Zeiger sind fast immer ein Fehler.


Anmelden zum Antworten