Zeiger auf ein Element aus std::map zurückgeben


  • Gesperrt

    Ich raffs nicht. Wieso nicht einfach so was:

    #include <iostream>
    #include <vector>
    #include <map>
    
    struct A
    {
        uint32_t id;
        uint32_t irgendwas_anderes;
    };
    
    std::map<uint32_t, std::vector<A>> my_map;
    
    const std::vector<A> get_id(const uint32_t id)
    {
        std::map<uint32_t, std::vector<A>>::iterator iter = my_map.find(id);
        if (iter != my_map.end())
        {
            return iter->second;
        }
        return {};
    }
    
    int main()
    {
        my_map[1] = std::vector<A>{{8, 1111}};
        my_map[2] = std::vector<A>{{4, 111}, {8, 1111}};
        my_map[3] = std::vector<A>{{2, 11}, {4, 111}, {8, 1111}};
        my_map[4] = std::vector<A>{{1, 1}, {2, 11}, {4, 111}, {8, 1111}};
    
        std::cout << get_id(0).size() << std::endl;
        std::cout << get_id(4)[3].irgendwas_anderes << std::endl;
        std::cout << get_id(5).size() << std::endl;
    }
    


  • Dieser Beitrag wurde gelöscht!


  • @EinNutzer0 sagte in Zeiger auf ein Element aus std::map zurückgeben:

    const std::vector<A> get_id(const uint32_t id)

    Jedesmal eine Vectorkopie ausliefern?


  • Gesperrt

    @manni66 sagte in Zeiger auf ein Element aus std::map zurückgeben:

    @EinNutzer0 sagte in Zeiger auf ein Element aus std::map zurückgeben:

    const std::vector<A> get_id(const uint32_t id)

    Jedesmal eine Vectorkopie ausliefern?

    Stimmt, danke... also doch einen Zeiger zurückgeben.



  • Au backe! So viel kann ich wohl kaum auf einmal beantworten. Ich probier mich mal grob von meinem letzten Post aus vorzuhangeln.

    Also erst einmal als Grundsituation: Ich entwickle gerade auf dem Amiga mit dort leider noch einer alten Toolchain (GCC 4.2.4), da ich das Projekt vor Jahren begonnen hatte und es dazwischen eingeschlafen war (meine Hobbies alternieren 😉 ). Das schränkt die C++ Möglichkeiten schon mal ein.

    Einen Vector von Zeigern habe ich gemacht, weil ich nicht will, dass ewig hin und her kopiert wird, da dies Laufzeit kostet (und andere unschöne Nebenwirkungen hat, wenn man nicht aufpasst).
    Zurückgeben möchte ich einen Zeiger auf einen Vektor, da die dazugehörenden Frames mehrfach verwendet werden sollen, in verschiedenen Animationen (die darzustellenden Frames sind immer die gleichen, jede Animation kann aber z.B. zeitlich anders ablaufen usw.) - auch um etwas effizienter zu sein und nicht jedesmal die gleichen Frames neu im Speicher anzulegen. Das Ganze ist natürlich nur ein Auszug aus einem viel größeren Kontext (Spiel) - eine ältere Version kann man hier begutachten (die Website ist bewusst so gestaltet, damit sie auf Amigas mit älteren Browsern angezeigt werden kann).

    Derzeit schaue ich gerade, ob ich mir eine aktuellere Toolchain installiere (gcc 😎 und dann nach und nach versuche, umzustellen (auf auto, range based fors, usw. usf.). Allerdings kenne ich die neueren Konzepte noch weniger als die alten (bin nach wie vor blutiger C++ Anfänger und meine C-Jahre liegen auch schon sehr lange zurück!).

    Hoffe, das erklärt das Ganze ein bisschen...


  • Mod

    Wenn du mehr Indirektion aus Performancegründen einsetzt, dann erreichst du genau das Gegenteil von dem, was du möchtest. Jetzt liegt dein Zeugs kreuz und quer im Speicher, und bei jedem Zugriff muss erst einmal Schnitzeljagd gespielt werden, um an die eigentlichen Nutzdaten zu kommen. Kopier einfach deinen Vector nicht! Ist ganz einfach. Du könntest beispielsweise stets nur indirekt darauf verweisen 🙂

    Ansonsten: Das wichtige Grundkonzept, aus das alle hier hinaus wollen, und das dir wahrscheinlich nicht so ganz geläufig ist, ist "RAII". Aber besser eigentlich, wenn man selbst das gar nicht erst braucht, die sogenannte "Rule of Zero". (Alles gut googelbare Begriffe)

    Allgemein ist es eigentlich ein eher schlechtes Zeichen, wenn du in C++ vielen Zeigern begegnest. Unter der Haube ist natürlich vieles ein verkappter Zeiger, aber wenn du selber viele * in deinem Programm benutzt, dann ist oft etwas falsch. C++ ist halt doch nicht C mit class und cout. Viel krasser noch mit new oder gar malloc. Die mögen zwar auch unter der Haube vorkommen (macht beispielsweise der Vector intern), aber wenn das jemals explizit in einem Programm steht (Wie sind beispielsweise deine Zeiger initialisiert worden?) dann gilt das quasi als Fehler in C++. Siehe oben zu RAII und Rule of 0.

    Ich kann dir aber natürlich nicht in 5 Minuten C++ beibringen 🙂



  • @Reth sagte in Zeiger auf ein Element aus std::map zurückgeben:

    die darzustellenden Frames sind immer die gleichen, jede Animation kann aber z.B. zeitlich anders ablaufen usw.

    Wenn dem so ist und sich die Frames in frameCollections zur Laufzeit sowieso nicht ändern, dann kannst Du gleich die Instanzen direkt im Vektor ablegen ohne gezeigere. Den einzelnen Animationen kannst Du dann Iteratoren von frameCollections geben, wie glaube ich weiter oben schon angemerkt wurde.

    ... und

    @SeppJ sagte in Zeiger auf ein Element aus std::map zurückgeben:

    Kopier einfach deinen Vector nicht!

    das.


  • Mod

    Da es mir gerade noch einfällt: Das was ich und Swordfish gesagt haben, impliziert natürlich, dass ein Objekt, falls es denn wirklich sooo super groß ist, dass selbst eine interne Reallokation des Vectors eine Problem darstellen könnte, trotzdem nicht vom Programmierer mittels eines Pointers angesprochen werden sollte. In dem Falls sollte das Objekt selber dafür Sorge tragen (folgt aus RAII, da es selber für sich verantwortlich ist), dass es günstig verschiebbar ist, etwa indem es intern nur indirekt auf seine Ressourcen zeigt. Und das macht es natürlich nicht selber selber, sondern indem es diese Indirektion und Verschiebung auf eine dafür vorgesehene Halterklasse verlagert (Das ist dann die Rule of Zero).

    Das macht beispielsweise der Vector selber, der schließlich beliebig groß sein könnte und entsprechend teuer zu verschieben. Der biegt dann nämlich nur seine internen Zeiger um, ohne dass am internen Speicherplatz etwas gemacht wird. Aber das hilft natürlich nicht, wenn man explizit eine Kopie eines Vectors macht, denn dann muss ja irgendwann einmal wirklich alles kopiert werden, was teuer werden kann und daher vermieden werden sollte. Zum Glück geht das aber immer zu vermeiden, außer man braucht wirklich eine echte Kopie.



  • Danke euch. Dass mir hier auf die Schnelle nicht C++ beigebracht werden kann ist mir schon klar.

    Und tatsächlich: Ich verwende new und delete - aber aus o.a. Grund: Aktuell arbeite ich auf einer recht alten Toolchain mit GCC 4.2.4, da ist noch nix mit auto, sharedpointer usw. Da muss man seinen Haushalt noch selbst im Griff haben 😁 (Spass) - allerdings bin ich mir der damit verbundenen Gefahren (v.a. mit dem Thema Kopieren) schon bewusst (ob ich sie im Griff habe: Hoffentlich).

    Dann mach ich mich mal mit der Rule of 0 und RAII vertraut und hoffe, ich kapiere es und weiss es dann auch anzuwenden!

    Dazu schon mal eine erste Frage:
    Mit der Rule of Zero bzw. den dazugehörenden Rules of 3, 5, 6 - wie gehe ich denn dann mit der Datenhaltung zur Laufzeit um? Diese scheint mir dadurch extrem erschwert, wenn z.B. im Konstruktor Daten übergeben (bisher habe ich dafür keine eigenen Copy-Konstruktoren geschrieben) oder aber im Destructor Ressourcen freigegeben werden (dadurch, dass ich System-API nutze muss ich dafür benötigte Datenstrukturen selbst anlegen, inkl. Speicherreservierung und später auch wieder freigeben)?
    Scheint so, als ob hier C++ seine eigenen Konzepte im Weg stehen (aber ist meine aktuelle Sicht als Newcomer)?



  • @Reth sagte in Zeiger auf ein Element aus std::map zurückgeben:

    Dann mach ich mich mal mit der Rule of 0 und RAII vertraut und hoffe, ich kapiere es und weiss es dann auch anzuwenden!

    Es wäre viel einfacher wenn Du das zuerst machen würdest und danach gezielt Fragen stellen würdest.

    RAII und Rule of Three im Schnellverfahren:
    Eine Klasse hat die Aufgabe eine Ressource zu verwalten (nehmen wir den Evergreen Speicher):

    class foo
    {
        int *data;
        std::size_t size;
    
    public:
        // default-ctor:
        foo() : data(nullptr), size(0) {}
    
        // default-copy-ctor tut nicht, weil der nur den Wert des Pointers kopieren würde.
        // Gäbe ein mehrfaches delete wenn mehrere Destruktoren den selben Pointer freigeben wollen.
        // Also copy-ctor der eine "tiefe" kopie macht:
        foo(foo const &other) : data(nullptr), size(other.size)
        {
            data = new int[other.size];
            std::copy(other.data, other.data + other.size, data);
        }
    
        // Für assignment so ziemlich dasselbe (siehe bitte aber das copy-and-swap-idiom für elegante strong exception safety):
        foo& operator=(foo const &other)
        {
            if (this == &other)
                return *this;
    
            int *temp_data = new int[other.size];
            std::copy(other.data, other.data + other.size, temp_data);
    
            delete[] data;
            data = temp_data;
            size = other.size;
            return *this;
        }
    
        // destructor:
        ~foo() { delete[] data; }
    };
    

    Die Regel ist nun, daß wenn Du einen der drei (copy-ctor, assignment-operator, destructor) selbst implementierst weil das Compilergenerierte default nicht ausreicht, mußt Du höchstwahrscheinlich auch die anderen beiden selbst implementieren.



  • Touché. Da war ich zu vorschnell bzw. ist der 2. Teil meiner Frage der relevantere: Freigabe von Ressource im Destructor. Denn es ging ja auch mit um die Rule of 0.

    Allerdings hab ich noch nicht wirklich (für mich) gute Beschreibungen zu dieser Regel gefunden. Die auf dieser Seite besagt aber, dass Klassen, die einen Custom-Destruktor haben, sich selbst um die Ownership kümmern müssen (nehme an, dabei handelt es sich um die Verantwortung ihrer Member gegenüber, z.B. wenn es um Copy-Konstruktor usw. geht). Dies entspricht ja auch dem Beispiel oben (eigene Implementierung von copy-ctor + Zuweisungsoperator).
    Da bei mir die Frame-Klasse einen custom-Destruktor hat, da darin Systemressourcen freigegeben werden, verletzt diese Klasse die Rule of Zero schon mal (soweit ich das verstanden habe). Oder liege ich da total daneben?
    Hier ist korrekt, dass ich noch den Fehler habe, dass ich bisher weder CopyCtor noch Zuweisungsoperator selbst implementiert habe. Dies müsste ich dann noch nachholen.

    Um dies zu vermeiden: Was ist denn da die geeignetere Vorgehensweise? Ich muss leider bei mir mit Allockieren und Freigeben von BitMaps und anderen Systemressourcen umgehen, da komm ich nicht dran vorbei. Die Frage ist: Wo mach ich dieses dann geeigneterweise, wenn nicht in Klassen? Bisher hab ich da Klassen drum gebaut, die das ganze kapseln.



  • @Reth sagte in Zeiger auf ein Element aus std::map zurückgeben:

    Ich muss leider bei mir mit Allockieren und Freigeben von BitMaps und anderen Systemressourcen umgehen, da komm ich nicht dran vorbei. Die Frage ist: Wo mach ich dieses dann geeigneterweise, wenn nicht in Klassen? Bisher hab ich da Klassen drum gebaut, die das ganze kapseln.

    Ja, genau so. Und Klassen die Objekte dieser deiner resourcenverwaltenden Klassen halten können dann der Rule of Zero folgen, weil sie sich nicht mehr um Resourcenverwaltung kümmern müssen.

    struct foo { char *str; /* ein "String" */ };  // foo muss für diesen "String" kindermädchen spielen.
    struct bar { std::string str; /* ein String */ }; // bar braucht sich um nichts zu kümmern weil das std::string macht.
    


  • @Swordfish sagte in Zeiger auf ein Element aus std::map zurückgeben:

    Ja, genau so. Und Klassen die Objekte dieser deiner resourcenverwaltenden Klassen halten können dann der Rule of Zero folgen, weil sie sich nicht mehr um Resourcenverwaltung kümmern müssen.

    Alles klar, danke! Dann hab ich das ja schon mal halbwegs richtig verstanden.
    Heißt, ich muss bei FrameC und meinen weiteren "Kapsel-Klassen" noch die zusätzlich benötigten Default-Funktionen/-operatoren überschreiben. - Na hoffentlich bekomme ich das hin, gerade wenn überall noch Dinge vom System allokiert und wieder freigegeben werden müssen ...
    Das war übrigens ein weirerer Grund, wieso ich in den STL-Containern und an anderen Stellen Zeiger bzw. Referenzen verwendet habe - damit die Kopiererei von Systemressourcen ausbleibt.

    Dazu noch eine Frage: Wäre in solchen Fällen (also wenn Klassen gekapselt Systemressourcen, wie filehandles, BitMapStrukturen usw. halten) eher eine flache Kopie angesagt mit der Nutzung von Zeigern auf diese Ressourcen (gefühlt scheint mir das da eher der richtige Ansatz zu sein)? Das würde aber dann auch bedeuten, dass die Member solcher Ressourcen in den gekapselten Klassen, die im Konstruktor initialisiert oder im dtor freigegeben werden auch nur als Zeiger angelegt werden könnten (oder hab ich da nen Denkfehler)?
    Wieso spielt es denn keine Rolle, wenn ich neben dem Standard-Konstruktor noch einen Custom habe? In diesen Fällen müsste ich doch den Copy-ctor und Zuweisungsoperator doch auch überschreiben, damit tiefe Kopien gemacht werden, oder nicht? Bei solchen Klassen wöllte ich z.T. gar nicht, dass es default-Konstruktoren gibt, da sie immer nur zur Laufzeit mit initialisierten Membern vorkommen dürfen. Unterdrückt man dann den default ctor?

    Und für meine Eingangsfrage noch ein Punkt: Wäre es in dem Fall (neben der Möglichkeit Iteratoren zurückzugeben) denn nicht geeigneter, statt eines Zeigers eine konstante Referenz auf den konstanten Vektor zurückzugeben?


  • Mod

    Viele Fragen, für die ich gerade nicht genug Zeit habe (später…), aber eines muss gesagt werden: Eine alte Toolchain ist keine Ausrede. Dies sind Konzepte, die in C++ von Anfang an drin waren und die treibenden Ideen hinter seiner Erfindung sind. Und GCC 4.2 ist von 2008! Das wovon wir hier reden ist aus den 1980ern und 1990ern.


  • Mod

    @Reth sagte in Zeiger auf ein Element aus std::map zurückgeben:

    @Swordfish sagte in Zeiger auf ein Element aus std::map zurückgeben:

    Ja, genau so. Und Klassen die Objekte dieser deiner resourcenverwaltenden Klassen halten können dann der Rule of Zero folgen, weil sie sich nicht mehr um Resourcenverwaltung kümmern müssen.

    Alles klar, danke! Dann hab ich das ja schon mal halbwegs richtig verstanden.
    Heißt, ich muss bei FrameC und meinen weiteren "Kapsel-Klassen" noch die zusätzlich benötigten Default-Funktionen/-operatoren überschreiben. - Na hoffentlich bekomme ich das hin, gerade wenn überall noch Dinge vom System allokiert und wieder freigegeben werden müssen ...

    Wahrscheinlich gibt es das alles schon fertig in deiner Standardbibliothek (weiß nicht mehr genau, auf welchem Stand GCC4.2 ist) und du musst gar nichts mehr selber machen. Das ist der Kerngedanke der Rule of Zero. Und selbst wenn es die nicht gibt: Wieso sollte ein Frame sich um Ressourcenverwaltung kümmern? das ist nicht seine Aufgabe. In dem Fall schreibst du einmalig eine kleine Halterklasse, die das korrekt macht, und die benutzt dein Frame dann. Weil die klein und einfach ist, ist es leicht, diese korrekt zu programmieren, wohingegen es unnötiger Blähcode wäre, die gleiche Aufgabe in den Frame zu integrieren.

    Das war übrigens ein weirerer Grund, wieso ich in den STL-Containern und an anderen Stellen Zeiger bzw. Referenzen verwendet habe - damit die Kopiererei von Systemressourcen ausbleibt.

    Und wenn der Anwender davon nichts weiß, macht er es nicht so, und bekommt ein Problem. Daher sollte sich jede Klasse selber korrekt um ihre Ressourcen kümmern und sich nicht darauf verlassen, dass der Anwender Bescheid weiß, wie die Klasse intern funktioniert (Oder schlimmer noch: Was ist wenn sich die Interna verändern? )

    Dazu noch eine Frage: Wäre in solchen Fällen (also wenn Klassen gekapselt Systemressourcen, wie filehandles, BitMapStrukturen usw. halten) eher eine flache Kopie angesagt mit der Nutzung von Zeigern auf diese Ressourcen (gefühlt scheint mir das da eher der richtige Ansatz zu sein)? Das würde aber dann auch bedeuten, dass die Member solcher Ressourcen in den gekapselten Klassen, die im Konstruktor initialisiert oder im dtor freigegeben werden auch nur als Zeiger angelegt werden könnten (oder hab ich da nen Denkfehler)?

    Kommt auf die Ressourcen an. Als Faustregel gilt: Mach es so wie die Standardbibliothek. Allgemein werden flache Kopien vermieden, da das eine unerwartete Semantik ist. Da sind File-Objekte dann lieber gar nicht kopierbar, als dass man auf einmal zwei Objekte hat, die sich auf die gleiche (nicht duplizierbare) Datei beziehen. Dinge, die große, aber kopierbare Ressourcen verwalten (z.B. Vector oder String) können natürlich nicht verhindern, dass jemand eine explizite tiefe Kopie macht, aber sie unterstützen ausdrücklich, dass sie effizient "movable" sind.

    Wieso spielt es denn keine Rolle, wenn ich neben dem Standard-Konstruktor noch einen Custom habe? In diesen Fällen müsste ich doch den Copy-ctor und Zuweisungsoperator doch auch überschreiben, damit tiefe Kopien gemacht werden, oder nicht? Bei solchen Klassen wöllte ich z.T. gar nicht, dass es default-Konstruktoren gibt, da sie immer nur zur Laufzeit mit initialisierten Membern vorkommen dürfen. Unterdrückt man dann den default ctor?

    ??? Ich verstehe nicht, was du hier fragst. Hier sind die Regeln und die machen so auch Sinn:
    https://mariusbancila.ro/blog/2018/07/26/cpp-special-member-function-rules/
    Defaultkonstruktoren kannst du haben wie du lustig bist, da ist nichts besonderes dran. Für die anderen 5 gilt: Wenn du meinst, einen davon zu brauchen, dann brauchst du höchstwahrscheinlich auch all die anderen (Rule of 5, früher (vor Move) Rule of 3). Aber wie schon oben gesagt, normalerweise brauchst du keinen einzigen davon (Rule of 0).

    Und für meine Eingangsfrage noch ein Punkt: Wäre es in dem Fall (neben der Möglichkeit Iteratoren zurückzugeben) denn nicht geeigneter, statt eines Zeigers eine konstante Referenz auf den konstanten Vektor zurückzugeben?

    Ginge auch, aber gibt es irgendeinen Grund dafür gegen den Iterator? Der ist eigentlich genau dafür da, um das zu erreichen, was du hier versuchst.
    Du verlierst natürlich die Eigenschaft, dass der Zeiger auch 0 sein kann, aber eigentlich wirft das eher die Frage auf, wieso das bei dir überhaupt drin ist. Ist das wirklich ein normaler Programmablauf, wenn ein Framevector nicht gefunden werden kann? Ist das nicht vielleicht eher ein Ausnahmefall (-> Exception)?

    Wieso überhaupt so ein alter Compiler? Ist doch nicht so, als ob es für den Amiga keine aktuellen Entwicklungswerkzeuge gäbe.



  • @SeppJ sagte in Zeiger auf ein Element aus std::map zurückgeben:

    Wahrscheinlich gibt es das alles schon fertig in deiner Standardbibliothek (weiß nicht mehr genau, auf welchem Stand GCC4.2 ist) und du musst gar nichts mehr selber machen. Das ist der Kerngedanke der Rule of Zero.

    Leider nein, die Standardbibliothek bietet dazu nichts an (hier geht es um Elemente des OS, die sich mit der GUI Intutition befassen.

    Und selbst wenn es die nicht gibt: Wieso sollte ein Frame sich um Ressourcenverwaltung kümmern? das ist nicht seine Aufgabe. In dem Fall schreibst du einmalig eine kleine Halterklasse, die das korrekt macht, und die benutzt dein Frame dann. Weil die klein und einfach ist, ist es leicht, diese korrekt zu programmieren, wohingegen es unnötiger Blähcode wäre, die gleiche Aufgabe in den Frame zu integrieren.

    Der Frame ist in diesem Fall bereits die kleine Halterklasse. Er hat und kann nicht viel und kapselt intern eine BitMap, die sich um die grafische Darstellung des Frameinhalts kümmert. Diese muss entsprechend der Dimensionen des Frames und weiterer Parameter initiiert und allokiert werden. Danach muss noch die entsprechende Grafik in die BitMap gebracht werden. Eine BitMap muss dabei durch den Programmierer allokiert und am Ende wieder frei gegeben werden. Das Ganze wird im Frame zusammen mit ein paar Größenangaben gekapselt. (Die zeitliche Steuerung hab ich nicht im Frame, sondern in meiner Animationenklasse.) Damit ein Frame geblittet werden kann, gibt er dann noch 2 private Zugriffe auf interne Daten, die eine Friendklasse nutzen kann. Das war schon alles.

    Das war übrigens ein weirerer Grund, wieso ich in den STL-Containern und an anderen Stellen Zeiger bzw. Referenzen verwendet habe - damit die Kopiererei von Systemressourcen ausbleibt.

    Und wenn der Anwender davon nichts weiß, macht er es nicht so, und bekommt ein Problem. Daher sollte sich jede Klasse selber korrekt um ihre Ressourcen kümmern und sich nicht darauf verlassen, dass der Anwender Bescheid weiß, wie die Klasse intern funktioniert (Oder schlimmer noch: Was ist wenn sich die Interna verändern? )

    Den Punkt hab ich in diesem Zusammenhang leider nicht verstanden.

    Dazu noch eine Frage: Wäre in solchen Fällen (also wenn Klassen gekapselt Systemressourcen, wie filehandles, BitMapStrukturen usw. halten) eher eine flache Kopie angesagt mit der Nutzung von Zeigern auf diese Ressourcen (gefühlt scheint mir das da eher der richtige Ansatz zu sein)? Das würde aber dann auch bedeuten, dass die Member solcher Ressourcen in den gekapselten Klassen, die im Konstruktor initialisiert oder im dtor freigegeben werden auch nur als Zeiger angelegt werden könnten (oder hab ich da nen Denkfehler)?

    Kommt auf die Ressourcen an. Als Faustregel gilt: Mach es so wie die Standardbibliothek. Allgemein werden flache Kopien vermieden, da das eine unerwartete Semantik ist. Da sind File-Objekte dann lieber gar nicht kopierbar, als dass man auf einmal zwei Objekte hat, die sich auf die gleiche (nicht duplizierbare) Datei beziehen. Dinge, die große, aber kopierbare Ressourcen verwalten (z.B. Vector oder String) können natürlich nicht verhindern, dass jemand eine explizite tiefe Kopie macht, aber sie unterstützen ausdrücklich, dass sie effizient "movable" sind.

    Ok, was wäre dann der Weg für Ressourcen wie Fenster, Screen, BitMap usw.? Ich wäre für nicht-kopierbar machen, weil unvorhersehbares Verhalten ...

    Wieso spielt es denn keine Rolle, wenn ich neben dem Standard-Konstruktor noch einen Custom habe? In diesen Fällen müsste ich doch den Copy-ctor und Zuweisungsoperator doch auch überschreiben, damit tiefe Kopien gemacht werden, oder nicht? Bei solchen Klassen wöllte ich z.T. gar nicht, dass es default-Konstruktoren gibt, da sie immer nur zur Laufzeit mit initialisierten Membern vorkommen dürfen. Unterdrückt man dann den default ctor?

    ??? Ich verstehe nicht, was du hier fragst. Hier sind die Regeln und die machen so auch Sinn:
    https://mariusbancila.ro/blog/2018/07/26/cpp-special-member-function-rules/

    Also ich bin sie durchgegangen und leider ergibt für mich die Erklärung, der YES und NO aus der Übersichttabelle keinen Sinn, da beide Werte jeweils 2 voneinander abweichende Bedeutungen haben. YES kann heißen "the special member function is defined by the compiler" oder "the special member function is defined by the compiler but this is deprecated and may be removed in the future" (bei NO gibt es ebenfalls nochmal 2 abweichende Erklärungen). Welche der beiden gilt denn nun bei YES bzw. dann bei NO? Das kommt für mich aus der Übersichtstabelle leider nicht raus!

    Defaultkonstruktoren kannst du haben wie du lustig bist, da ist nichts besonderes dran. Für die anderen 5 gilt: Wenn du meinst, einen davon zu brauchen, dann brauchst du höchstwahrscheinlich auch all die anderen (Rule of 5, früher (vor Move) Rule of 3). Aber wie schon oben gesagt, normalerweise brauchst du keinen einzigen davon (Rule of 0).

    Ich hatte hier nach Custom-Konstruktoren gefragt, nicht nach Default! Mit Custom meine ich, dass diese Werte bei der Initialisierung entgegennehmen und in internen Variablen speichern. Hier war dann meine Annahme, dass ich doch in solchen Fällen dann auch den Copy-ctor (und wohl auch den Move-ctor) überschreiben muss - oder werden alle Member automatisch mitkopiert (soweit ich weiss, "nur" triviale)?

    Und für meine Eingangsfrage noch ein Punkt: Wäre es in dem Fall (neben der Möglichkeit Iteratoren zurückzugeben) denn nicht geeigneter, statt eines Zeigers eine konstante Referenz auf den konstanten Vektor zurückzugeben?

    Ginge auch, aber gibt es irgendeinen Grund dafür gegen den Iterator? Der ist eigentlich genau dafür da, um das zu erreichen, was du hier versuchst.

    Ich möchte die Sammlung von Frames, die zu einer Animation gehören in dem betroffenen Animationsobjekt als Member nutzen (Idee war: Ohne Kopie). Jedes Animationsobjekt weiss selbst, welchen Frame es als nächsten nutzen will (daher dachte ich an den Vector anstelle eines Iterators). Die Frames selbst gibt es aber nur einmal für alle Animationen (das ist ausreichend).

    Wieso überhaupt so ein alter Compiler? Ist doch nicht so, als ob es für den Amiga keine aktuellen Entwicklungswerkzeuge gäbe.

    Das Projekt habe ich schon 2011/2012 (oder noch früher) begonnen. Ob es damals schon modernere Toolchains gab weiss ich leider nicht (mir waren keine bekannt). Habe erst dieser Tage wieder gesucht und die aktuellen adtools mit dem GCC 8 gefunden. Dazu müsste ich aber erstmal meinen ganzen Code "modernisieren", wobei ich noch nicht weiss, welchen Ansatz man da am besten fährt.

    Bei dem Thema mit der alten Toolchain gabs evlt. ein Missverständnis. Diese habe ich im Kontext "man sollte kein new/delete mehr brauchen" angeführt. Ersatz dafür gab es erst mit späteren Standards (weiß gerade nicht mal auswendig, welches der neueren Konzepte/Tools dafür sorgte). In dem Zshg. hab ich die alte Toolchain angeführt und im Zshg. mit dem Move-Konstrukt, welches auch später kam. Den Rest gab es schon im alten GCC, das ist richtig.



  • @Reth sagte in Zeiger auf ein Element aus std::map zurückgeben:

    Eine BitMap muss dabei durch den Programmierer allokiert und am Ende wieder frei gegeben werden.

    Hast Du mal einen Link zur Doku der entsprechenden Funktionen?

    @Reth sagte in Zeiger auf ein Element aus std::map zurückgeben:

    Dazu müsste ich aber erstmal meinen ganzen Code "modernisieren", wobei ich noch nicht weiss, welchen Ansatz man da am besten fährt.

    Warum? Wieso soll Dein Code nicht auch als Hausnummer C++14 kompilieren?

    @Reth sagte in Zeiger auf ein Element aus std::map zurückgeben:

    (weiß gerade nicht mal auswendig, welches der neueren Konzepte/Tools dafür sorgte)

    Smartpointer + std::make_shared<>() undstd::make_unique<>()`



  • @Swordfish sagte in Zeiger auf ein Element aus std::map zurückgeben:

    Hast Du mal einen Link zur Doku der entsprechenden Funktionen?

    Hier mal die Version des AOS3.x, die ich nutze, um abwärtskompatibel zu sein:
    AllocBitMap
    Der Link zur Freigabefunktion ist unten auf der Seite angegeben.
    Für das "Einsetzen" der Grafik nutze ich dann noch einen RastPort (der muss mit InitRastPort initialisiert werden), lokale temporäre BitMaps und WriteChunkyPixels, sowie GetBitMapAttr und CopyMem).
    Hier hab ich auch schon erste Inkonsistenzen, da ich noch eine RastPort-Kapselklasse gemacht habe, um einfachere Methoden für Clipping und Blitten anzubieten (aber die sind wohl dann zu "einfach", da noch nicht alle möglichen Parametrierungen nach außen gelegt sind - sie können also viel weniger als die Originalfunktionen, -strukturen etc.). Dazu nutze ich diese RastPort-Kapselklasse z.B. nicht in Frame für die Initialisierung, da mir dort eine einfache RastPort-Struktur des Systems reicht (sie wird nur als Zwischenpuffer genutzt - Objekte der Kapselklasse kapseln eher die Funktionen, die das System für RastPorts anbietet, nicht die RastPort-Struktur).
    Aber zu den Punkten kann ich noch sehr viel sagen, das Design ist sicher nicht so prall.

    Warum? Wieso soll Dein Code nicht auch als Hausnummer C++14 kompilieren?

    Na das sollte er! ☺ Ich dachte da eher so an Sachen, wie Nutzung von auto, anstelle der kaum lesbaren alten Schreibweise für STL-Iteratoren, smartpointer, Nutzung von Range-based-for, Initializer-Arrays, constexpr und was es noch so alles gibt. Keine Ahnung, ob ich die auch wirklich alle nutzen sollte, bzw. in welcher Reihenfolge man die dann am besten bzgl. Portierung angeht.



  • Gerade solche Allokations- und Deallokationsfunktionen (als Paar) sind doch perfekt für RAII, also diese beide in einer Klasse kapseln.
    Und dann kannst du ein Objekt dieser Klasse direkt in der Anwendungsklasse (z.B. FrameC) benutzen.



  • @Th69 sagte in Zeiger auf ein Element aus std::map zurückgeben:

    Gerade solche Allokations- und Deallokationsfunktionen (als Paar) sind doch perfekt für RAII, also diese beide in einer Klasse kapseln.
    Und dann kannst du ein Objekt dieser Klasse direkt in der Anwendungsklasse (z.B. FrameC) benutzen.

    Also wenn ich mir den Wiki-Artikel anschaue, dann entspricht doch FrameC schon dem Pattern, oder nicht?
    Im Wiki Bsp. wird eine Dateiklasse erstellt, welche die Verwaltung der Systemressource FILE übernimmt. Darin enthalten ist auch eine Ausgabe (also nicht nur reines öffnen und schließen). Bei FrameC ist es quasi ähnlich. Die Klasse verwaltet die BitMap und ist gleichzeitig auch für die Darstellung gedacht (kann geblittet werden). Was wäre denn der Vorteil, wenn ich da noch eine Indirektion einführe und eine weitere Klasse mache, die nur eine BitMap alloziert und wieder freigibt?
    Ich müsste quasi alle Parameter des FrameC-Konstruktors and diese neue Klasse weiterreichen und auch die Hälfte der getter (für die Bild- und Maskendaten). Damit ist dann fast alles doppelt vorhanden (ich kann die Klasse auch gern mal hier rein stellen).

    Allerdings habe ich an anderen Stellen, bei denen ich BitMaps und andere Systemressourcen nur kurz als Hilfsmittel verwende, diese direkt in den Methoden benutzt (da ich sie dort nicht als Frame gebraucht habe, sondern, um z.B. temporär einen Puffer fürs double-buffering zu erzeugen).


Anmelden zum Antworten