Referenzen auf im vector verwaltete Elemente



  • Hi,

    ich habe einen Fehler gemacht, ich habe das getan, was ich im Topic beschrieb: Referenzen auf im vector verwaltete Elemente vergeben. Später ändere ich den vector -> Referenzen ungültig. Und bis ich das Problem gefunden hatte, verging auch noch einige Zeit. 🙄

    Die Frage lautet: Wie löst ihr das architektonisch oder wieso passieren euch solche Probleme nicht? Habt ihr schon einen Satz im Kopf wie "Referenzen auf Vector-Elemente - VORSICHT"? Ich glaube, den habe ich ab jetzt (hatte ich, aber er war nicht laut genug)

    Und wie löst ihr das, wenn ihr die Referenzierung beibehalten wollt? Einfach eine list statt einem vector nehmen?

    Die Fragen erscheinen trivial, aber ich würde gerne hören, ob jemandem (wie so oft :)) etwas einfällt, was mich wieder etwas erhellen kann.

    Vielen Dank und beste Grüße!



  • Das lässt sich so pauschal nicht beantworten, es hängt von der konkreten Aufgabe ab.

    Evtl. ist ein shared_ptr passend für deine Situation. Dieser ist beliebig kopierbar und RAII ist gleich mit eingebaut 😉



  • Das kommt drauf an, was Du am Vektor änderst. Du könntest zum Beispiel einfach den Index des Objekts im Vektor benutzen, anstatt eine Referenz auf das Objekt.



  • Vielleicht so:

    struct Knoten : boost::noncopyable
    {
      std::vector<Knoten*> nachbarn;
    };
    
    struct Graph : boost::noncopyable
    {
      boost::ptr_vector<Knoten> alle_knoten;
    };
    


  • Eisflamme schrieb:

    Und wie löst ihr das, wenn ihr die Referenzierung beibehalten wollt? Einfach eine list statt einem vector nehmen?

    Offen gesagt sind die einzigen Fälle in denen ich sowas bisher brauchte immer container<unique_ptr<T>> gewesen, was das Problem irgendwie von alleine löst.



  • Sorry! Ich vergaß zu erwähnen, dass das durchaus copyable bleiben soll. Brauchte nur etwas, um wieder im Kopf zu haben, wieso ich das so machte.



  • Eisflamme schrieb:

    Ich vergaß zu erwähnen, dass das durchaus copyable bleiben soll.

    Das ändert im Prinzip nichts an den Vorschlägen. Wenn du z.b. so ein Graphobjekt kopieren willst, muss der entsprechende Kopierkonstruktor neue Knoten anlegen und diese auch neu verlinken. Mit 'nem automatisch generierten Konstruktor ist das schwer. 😉 boost::noncopyable bzw unique_ptr verhindert ja nur, dass die Dinger wie Kopierkonstruktor und Zuweisungsoperator automatisch generiert werden.



  • Bislang hat die autogenerierte Variante aber blendend funktioniert. Ich habe eine Art Tree, wobei es keine Polymorphie gibt. Wenn man die Referenzierung von außen außer acht lässt, dann kann ich einen neuen Root einfach mit einem anderen initialisieren und der automatische copy ctor erledigt, was ich möchte.

    Wenn ich nun statt container<Object> auf container<Object*> oder container<unique_ptr<Object>> oder boost::ptr_container<Object> wechsle, dann muss ich für meine Hierarchie überall eigene Constructoren und =-Operatoren einsetzen. Macht es da nicht mehr Sinn vector einfach durch list zu ersetzen? Oder wo genau liegt der Vorteil? Die Indirektion habe ich bei list<O> und bei vector<unique_ptr<O>> und Konsorten doch genau so. Die Konvertierbarkeit in ein Array benötigt ich nicht und die Iterationen selbst sind doch nicht schneller, wenn ich keinen Random Access nutze, oder? Und besonders viel steckt auch nicht je container drin, also ist so was wie Cache-Lokalität vermutlich auch zu vernachlässigen, oder? (wobei ich jetzt gar nicht weiß, ob und warum das bei list schlechter wäre)



  • Eisflamme schrieb:

    Referenzierung von außen

    Aha. Das ist eine neue Information für mich.

    Eisflamme schrieb:

    Wenn ich nun statt container<Object> auf container<Object*> oder container<unique_ptr<Object>> oder boost::ptr_container<Object> wechsle, dann muss ich für meine Hierarchie

    Was für eine Hierarchie?

    Eisflamme schrieb:

    überall eigene Constructoren und =-Operatoren einsetzen. Macht es da nicht mehr Sinn vector einfach durch list zu ersetzen?

    Das kann u.U. Sinn machen, ja.

    Eisflamme schrieb:

    Oder wo genau liegt der Vorteil?

    Hängt sicherlich davon ab, was du machen willst. bei boost::ptr_vector hast du allerdings noch random access und kopierbar ist das ding auch.



  • Okay, sorry, da hatte ich wieder Details vergessen.

    Also die Hierarchie ist so, dass ich quasi eine Klasse A, eine Klasse B und eine Klasse C habe. A hält einen container an Bs, B hält einen Container an Cs. Aufgebaut wird das durch jeweilige Add-Methoden, die B bzw. C als Parameter erwarten.

    ptr_vector klingt bei Kopierbarkeit dann wohl doch besser. Besonders viel einfügen und löschen tue ich nicht. Zwar kann man beides machen, aber nur auf einzelne Elemente, also performanceunkritisch.



  • Du solltest ein paar mehr Informationen rausrücken, wenn du hilfreiche Antworten haben möchtest. Ich verlange ja noch nichtmal ein kompilierfähiges Beispiel, aber
    zumindest die Randbedingungen solltest du schon angeben.



  • Ja, tut mir Leid, ich bin nie so ganz sicher, was relevant ist.

    Was wäre denn noch wichtig? Also die Hierarchie basiert eben auf den drei Klassen, bisher hat eben jede Klasse ein Attribut eines Vectors auf die jeweils niedrigere Klasse (also A hat vector auf B, B hat vector auf C, rückwärts nicht; also kein Graph, sondern nur ein Baum). Gehalten werden die Bäume (also Instanzen von A) von einer vierten Klasse, nennen wir diese Mal X. X ist nur einmal instanziiert (entspricht einem Tab im UI in einem nur einmal geöffnetem Fenster) und hat noch Instanzen von anderen Klassen. Niemand weiß, ob das hilft, aber diese benenne ich jetzt Mal mit Y und Z... und diese anderen Klassen halten eben Referenzen auf die As, Bs und Cs.

    Veränderungen der As, Bs und Cs geschehen einerseits durch Erweiterung (und außerhalb von Z... nämlich von dem, der Z instanziiert). Andererseits können in Y und Z auch Kinder von As (also Bs) gelöscht/geändert/hinzugefügt werden.

    Kopierbarkeit bietet sich deswegen an, weil man Duplikate erstellen können soll, die man dann leicht ändert. Einerseits zur Laufzeit, aber andererseits auch bei Initialisierung, weil ich dort die Hierarchie fix im Quellcode erstelle.

    Ich weiß nicht, ob das jetzt geholfen hat, die Strukturen sind ja schon eher komplex. Vielleicht irgendwas Bestimmtes, was ich noch beitragen kann?





  • Ich hab für genau so ein Problem mal ne Smartpointerklasse geschrieben. Die hält intern statt eines direkten Pointers auf das vector-Element einen Pointer auf den vector und den Index des Elements im vector. Lässt sich einfach als POD implementieren, mit allen möglichen Operatoren usw. Overhead ist die doppelte Größe im Vergleich zum C-Pointer und die zusätzliche Dereferenzierung.



  • Eisflamme schrieb:

    Die Frage lautet: Wie löst ihr das architektonisch oder wieso passieren euch solche Probleme nicht?

    Passiert mir nicht weil ich weiss dass es Probleme macht, und daher sowas nicht machen :^)
    Wie man es "löst" kommt drauf an was man genau braucht. Manchmal kann man einfach nen Index statt ner Referenz speichern, manchmal nimmt man ne std::deque statt nem Vektor, manchmal nimmt man shared_ptr oder unique_ptr oder ptr_vector, manchmal nimmt man ne map. Kommt halt drauf an...

    Und wie löst ihr das, wenn ihr die Referenzierung beibehalten wollt? Einfach eine list statt einem vector nehmen?

    std::list nehm ich quasi nie. Eher noch deque.
    Meistens mach ich aber einfach statt Referenzen was anderes, wie gesagt Index speichern oder map oder sowas.



  • Eisflamme schrieb:

    Also die Hierarchie basiert eben auf den drei Klassen ... (also A hat vector auf B, B hat vector auf C, rückwärts nicht ... Y und Z... und diese anderen Klassen halten eben Referenzen auf die As, Bs und Cs ... Andererseits können in Y und Z auch Kinder von As (also Bs) gelöscht/geändert/hinzugefügt werden

    Verpass deinen Objekten irgend wie eine Identität.
    Entweder über eine unveränderliche Adresse (implizites Kopieren/Moven verbieten, Pointer-Container oder Container aus Smart-Pointern).
    Oder über einen eindeutigen Schlüssel (also irgend ein Attribut oder Attribut-Tuple über das du sie eindeutig identifizieren und dann in einer Map/Set/... finden kannst).


Log in to reply