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



  • Desweiteren kannst du dir mal Gedanken machen, ob die ganzen Zeiger wirklich nötig sind. Grundsätzlich sollte man zuerst versuchen nur mit den Objekten selbst, bzw. Referenzen auf die Objekte, zu arbeiten.
    Wenn das wirklich nicht geht, dann sollte man über Smart-Pointer ( std::unique_ptr<T>, std::shared_ptr<T> ) nachdenken.

    Das nur als Hinweis, wenn du den nächsten Lern-Schritt gehen willst 😉



  • Ich kenne den Zusammenhang nicht, würde aber auch eher dazu tendieren den Iterator statt dem Pointer zurückzugeben. Es geht sonst relativ unnötig Information verloren, z.B. Die Map sollte z.b. Debugfunktionen haben um zu testen das der Iterator zur Map gehört. Würde auch eher zu keinen Pointern im Vector raten vermute aber das hat mit Vererbung zu tun.


  • Mod

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

    Würde auch eher zu keinen Pointern im Vector raten vermute aber das hat mit Vererbung zu tun.

    Wahrscheinlich ja. Wobei da direkt auffällt, dass das so aussieht, als wären hier die Besitzverhältnisse nicht geklärt. Weder wird hier ein Pointertyp verwendet, der die Besitzverhältnisse ausdrückt; noch ist ein roher Vector ein Typ, der Besitz auf indirekt verwiesene Ressourcen anmeldet. Daher wäre es schon interessant zu erfahren, was da genau dahinter steckt, denn höchstwahrscheinlich liegt hier ein Fehler vor. Der Threadersteller macht sich ja grundsätzlich Gedanken um Gültigkeit von Verweisen, daher wäre jetzt eine gute Gelegenheit, ihm die saubere Modellierung von Besitzverhältnissen beizubringen.


  • Mod

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

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

    Außer dass doppelte cv-Qualifizierer nicht erlaubt sind?

    Aja, die Ausnahme war ja long. ([dcl.spec]/2)

    Ähh, long mag zwar doppelt vorkommen dürfen, aber auch nur, weil es dann eine andere Bedeutung hat.



  • error: 'long long long' is too long for GCC

    https://www.youtube.com/watch?v=R2ml28eKMeg 🙂



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

    error: 'long long long' is too long for GCC

    https://www.youtube.com/watch?v=R2ml28eKMeg 🙂

    👍
    Ja, long long long ist falsch, korrekt wäre long long li long.
    https://www.youtube.com/watch?v=uc2UEfWjvo8


  • Mod

    long int long i;  // 100% korrektes C++
    

    Ahhh!

    PS:

     const long typedef int volatile long i;  // Auch 100% korrekt
    

    Vielleicht ist C++ doch nicht so schön wie gedacht.



  • int volatile long static unsigned const long i = 42;


  • 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;
    }
    


  • This post is deleted!


  • @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?



  • @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.
    

Log in to reply