Zeiger auf eigene Klasse möglich?



  • Hallo liebe Forenmitglieder!
    Alternativ zur Verwendung einer verketteten Liste würde ich gerne einen statischen Vector mit den Adressen der erstellten Instanzen einer Klasse erstellen. Dazu müsste ich aber im Konstruktor auf die Adresse der Instanz der Klasse zugreifen können. Ich konnte aber nichts finden.
    Wäre das nicht wesentlich praktischer als eine verkettete Liste, bei der das aufrufende Programm immer den Nachfolger bekannt geben muss?



  • @Rainer Die erste Gegenfarge wäre: Wozu meinst du das zu brauchen?



  • Warum nicht gleich einen std::vector<T>?
    Verkettete Listen sind bei modernen Computersystemen eher kontraproduktiv (was das Speichermanagement angeht).



  • @Tyrdal: Ich würde gerne (am ESP32) dynamisch Groupboxen erstellen. Wenn ich die Funktion SERVICE (periodisch) aufrufe, würde ich gerne alle Instanzen durchlaufen und diese bei Bedarf refreshen. Wenn ich mir einfach alle erstellten Instanzen in einem Vector (std::vector<myGroup*>) speichere, dann könnte ich diese einfach abarbeiten und müsste nicht wie bei verketteten Listen die nächste Groupbox angeben.
    Edit: Ich glaube ich suche nach (?):

      static std::vector<myGroup*> pvGroupInstance;
     ...
      myGroup* pThisGroupInstance = (this);
      pvGroupInstance.push_back(pThisGroupInstance);
    

    @Th69: Genau, mir scheint die verkettete Liste auch aufwändiger als meine geplante Lösung, da ich da den Nachfolger angeben muss...



  • Wie werden die Instanzen der Klasse myGroup verwaltet? Falls Du Angst vor dem Kopieraufwand von std::vector<myGroup>hast, wäre std::vector<std::unique_ptr<myGroup>> eine Alternative.



  • @john-0 Er nutzt sowieso Zeiger und der vector soll auch wohl nicht der Owner sein.



  • @Jockelx
    Ja, das ist die wahrscheinliche Vermutung. Nur ich würde das gerne bestätigt sehen.



  • @Rainer sagte in Zeiger auf eigene Klasse möglich?:

    myGroup* pThisGroupInstance = (this);
    pvGroupInstance.push_back(pThisGroupInstance);

    Wie wärs eigentlich mit:

    pvGroupInstance.push_back(this);
    

    ?



  • @john-0 sagte in Zeiger auf eigene Klasse möglich?:

    Wie werden die Instanzen der Klasse myGroup verwaltet? Falls Du Angst vor dem Kopieraufwand von std::vector<myGroup>hast, wäre std::vector<std::unique_ptr<myGroup>> eine Alternative.

    Sorry, habe ich erst jetzt gelesen. Ich hoffe ich verstehe die Frage richtig. Im Setup der main.c instanziere ich 3 Groupboxen:

      myGroup *grpSetting;
      myGroup *grpAntrieb;
      myGroup *grpCutter;
    

    Dank obigen Vektor, den ich mit jeder Instanzierung erweitere, kann ich nun mit einer übergeordneten Funktion den Service aller Instanzen ausführen. Ich finde es auch gut, dass ich die Groupboxen mit deren Namen ansprechen kann (und z.B. nicht über den Index eines weiteren Vektors).

    void myGUI::SERVICE(void)
    { 
      for (auto & GroupInstance : pvGroupInstance) {
        GroupInstance->SERVICE();   //Service aller Instanzen ausführen
      }
    }
    

    Mir scheint die Lösung momentan perfekt. Ehrlich gesagt weiß ich nicht, was Du mir mit der Alternative std::vector<std::unique_ptr<myGroup>> vorschlagen möchtest, könntest Du mir das bitte erklären?



  • Die erste Frage, die sich da stellt, ist die, ob man diese Gruppen tatsächlich auf dem Heap per new erzeugen muss, oder ob sie nicht einfach als Stackvariablen erzeugt werden können.

    In modernem C++ werden Praktiken wie

    class Demo
    {
       MyObject* obj_;
    
    public:
       Demo()
       {
          obj_ = new MyObject();
       }
    
       ~Demo()
       {
          delete obj_;
       }
    }
    // Was passiert, wenn das Objekt kopiert wird, also
    Demo d1;
    Demo d2 = d1;
    
    oder
    
    void func()
    {
       MyObject* obj = new MyObject();
       // do_something
       delete obj;
    }
    // Was passiert, wenn do_something eine exception wirft?
    

    als veraltet und fehleranfällig angesehen (Stichpunkte Rule of 3/5, exception safety) . Für sowas wurden smart Pointer wie std::unique_ptr und std::shared_ptr entwickelt, die die Lebenszeit eines Objekts verwalten. Du solltest dich unbedingt in das Thema RAII einlesen, wenn du jetzt nicht weißt, wovon ich rede.

    Die "richtige" Variante von func mit einem Objekt, das unbedingt auf dem Heap erzeugt werden muss, sähe so aus:

    void func()
    {
       std::unique_ptr<MyObject> obj = make_unique<MyObject>();
       // do something
    }
    

    Habt ihr coding guidelines? Funktionsnamen, die komplett großgeschrieben werden finde ich übelst, ich lasse mich von meinem Quelltext ungern anschreien. Zumal die Klassennamen CamelCase mit kleinem Anfangsbuchstaben benutzen. Sieht merkwürdig aus...



  • Aber wenn sein vector die Zeiger nicht besitzt, dann ist unique_ptr nicht die richtige Wahl.



  • Ich hatte bisher noch nie einen Anwendungsfall in dem eine (verkettete) Liste die Lösung war.

    Die wichtige Frage: soll der Vector die Zeiger besitzen, oder nicht? Willst du den Besitz des Pointers auf den gewünschten Container übergeben oder soll der vector nur eine Randnotiz sein und diese Zeiger eben auch kennen?

    Was sind das für Instanzen dieser Klasse? Sind das Pointer, die du von irgendeiner grafischen API bekommst oder legst du die Instanzen selbst an und hast die volle Kontrolle über die Lebenszeit der Instanzen?



  • @Tyrdael: Wird ja auch nirgendwo behauptet. Das einzige, das ich in Frage stelle ist das hier:

       myGroup *grpSetting;
       myGroup *grpAntrieb;
       myGroup *grpCutter;
    

    Müssen die Objekte zwingend auf dem Heap erzeugt werden? Geht das nicht als normale Stackvariable?
    Und falls diese Objekte unbedingt auf dem Heap erzeugt werden müssen, dann ist die Benutzung eines smart_ptr sinnvoll.



  • Danke Leute!
    @Tyrdal @DocShoe : Ich mache das (C++) nur als Hobby und bin daher nicht so fit wie Ihr. Daher auch keine (so strengen) coding guidelines. Da ich mit Arduino (ESP32) arbeite, ist nach meinem Verständnis die Klasse am Heap sinnvoll: Diese wird in der Regel in setup() initialisiert (bzw. durch den Konstruktor) und periodisch in loop() abgearbeitet. Wenn ich eine Initialisierung und einen Service in meiner Klasse brauche, sind diese Methode daher bei mir in der Regel groß.
    @It0101 sagte in Zeiger auf eigene Klasse möglich?:

    soll der Vector die Zeiger besitzen,

    Ich denke schon: Der Vektor merkt sich jede instanziierte Klasse und somit kann ich mit einer übergeordneten Funktion SERVICE() alle Methoden SERVICE() der instanziierten Klassen abarbeiten.

    @It0101 sagte in Zeiger auf eigene Klasse möglich?:

    sind das Pointer, die du von irgendeiner grafischen API bekommst oder legst du die Instanzen selbst an und hast die volle Kontrolle über die Lebenszeit der Instanzen

    Das ist eine von mir erstellte Klasse, die sich um die Darstellung einer selbst erstellten Groupbox kümmert (Text, Zeilen, Rahmen, Hintergrundfarbe,...)



  • @Rainer sagte in Zeiger auf eigene Klasse möglich?:

    Da ich mit Arduino (ESP32) arbeite, ist nach meinem Verständnis die Klasse am Heap sinnvoll: Diese wird in der Regel in setup() initialisiert (bzw. durch den Konstruktor) und periodisch in loop() abgearbeitet. Wenn ich eine Initialisierung und einen Service in meiner Klasse brauche, sind diese Methode daher bei mir in der Regel groß.

    Bei so kleinen Maschinchen würd ich persönlich eher dazu tendieren überhaupt nicht auf dem Heap zu arbeiten und nach Möglichkeit alles statisch zu machen (nicht zwangsweise auf dem Stack, aber durchaus vergleichbar). Das ganze new/delete will ja schliesslich auch verwaltet werden und so ein dynamischer malloc/free-Allokator unter der Haube hat schon einen gewissen Overhead (vor allem bezüglich Speicherbedarf für Metadaten und der Fragmentierung - je nachdem wie die new/delete-Aufrufe in deinem Programm verteilt sind).

    Da du das aber eh wie du sagst nur als Hobbyprojekt machst, geht das schon okay, solange du keine Speicherprobleme bekommst. Das ist immerhin etwas weniger mühsam als die gesamte Speichernutzung des Programms mit statisch reserviertem Speicher bis ins Detail durchzuplanen. Ich glaube die größeren Arduinos haben auch um die 320KiB Speicher, oder? Das ist ja richtig viel - habe bisher nur nen Arduino Nano programmiert - bei nur 2KiB Speicher überlegt man es sich zweimal, ob an sich wirklich einen dynamischen Speichermanager leisten will 🙂



  • @Finnegan Danke für die netten Worte und das Verständnis. Der ESP32 ist echt toll 520KB Ram - echt viel für ein so kleines Ding. Da kratze ich mit meinen Programmteilen irgendwo im % Bereich herum. Richtig rauf geht es mit diversen Libraries. Aber da ist noch viel Luft: RAM: [== ] 15.7% (used 83392 bytes from 532480 bytes).
    Enger wird es da schon beim Flahs (4MB), da ich 2 gleich große Partitionen und noch weitere kleine brauche:
    Flash: [========= ] 85.4% (used 1566759 bytes from 1835008 bytes). Aber notfalls habe ich noch die 16MB Version...

    Vielen Dank Leute, dass Ihr euer Wissen mit mir teilt. Ich wünsche euch ein schönes Weihnachtsfest!!! 🌠 🎄

    PS: Ein "kleines" Weihnachtsgeschenk hatte ich auch schon: 200KB Flash: Der ESP war noch auf 320KB eingestellt!