Map von Pointern auf Membervariablen



  • Habs gerade gemerkt. War Schwachsinn meinerseits.



  • Du musst unterscheiden zwischen

    *ptr = obj;
    

    und

    ptr = &obj;
    

    Sobald du diesen Unterschied siehst, sollte sich der Rest ergeben. Slicing entsteht nur bei Kopien oder Zuweisungen, nicht bei Zeigern und Referenzen.



  • Naja, im ersten Fall weist man dem Object auf das ptr zeigt den Wert von obj zu und im zweiten lässt man den ptr auf obj zeigen.
    Leider wurde ich mit Tomaten vor den Augen geboren, denn ich sehe trotzdem keine Lösung für mein Problem ;).
    Denn ich möchte ja einen Wert zuweisen. Also auf gut Deutsch. Was ich vorhabe geht nicht?
    Könnte ich das Slicing evtl. mit einem dynamic_cast verhindern? Die Sache mit der virtual-Funktion funktioniert nämlich leider wie du schon gesagt hast auch nicht.



  • Ich dachte, das hätte sich geklärt. Zeiger auf Membervariablen kann ja nicht das Problem sein, oder? Damit kein Slicing vorkommt, musst du eben Verweise und nicht Kopien benutzen.

    Kannst du nochmals dein momentanes Problem darstellen, am besten mit möglichst wenig drum herum, sodass man auf Anhieb verstehst, was du tun willst?



  • Double d = 320;
    Object *e = &d;
    std::wcerr << L"d before: '" << d << L"'" << std::endl;
    *e = Double(640);
    std::wcerr << L"d after: '" << d << L"'" << std::endl;
    

    Oder etwas komplizierter mit map:

    Double d = 320; //Diese Variable soll später zur Laufzeit verändert werden
    std::map<std::wstring, Object*> map; //dazu wird ein Zeiger auf sie mit einem Keywort vom Typ String
    map.insert(std::pair<std::wstring, Object*>(L"d", &d)); //in der Map eingefügt
    std::wcerr << L"d before: '" << d << L"'" << std::endl; // Ausgabe Wert d vorher
    std::map<std::wstring, Object*>::iterator it = map.find(L"d"); //Zeiger auf d in Liste ausfindig machen
    if (it != map.end()) // und wenn gefunden
    {
    	*(*it).second = Double(640); //hier irgendwie den Wert Double(640) der Variablen d zuweisen
    }
    std::wcerr << L"d after: '" << d << L"'" << std::endl; //Ausgabe geändertes d
    //Leider funktioniert das so nicht und d ist immer 320!
    


  • Wie schwierig kann eine vernünftige Problembeschreibung eigentlich sein? Ich habe die Frage nicht gestellt, nur damit du den gleichen Code nochmals ohne Erklärung postest. Abgesehen davon gibt es Dinge, die ich schon im ersten Post auf der ersten Seite erwähnt habe und die du immer noch ignorierst.

    Sorry, aber irgendwann wird es mir zu blöd, immer nachzufragen, weil du dein Problem nicht richtig beschreiben kannst. Wenn es wirklich nur um das Slicing geht, wurde dir meiner Meinung nach die Antwort schon längst gegeben.



  • Ganz ruhig, der Code lässt sich halt nicht weiter vereinfachen. Und das Problem habe ich jetzt auch schon mehrfach benannt. Habe jetzt nochmal Kommentare hinzugefügt. ich hoffe es wird jetzt klar was gewollt ist.
    Was wäre denn die Antwort auf das Slicing Problem?



  • So, was soll denn passieren, wenn der Object-Zeiger nicht auf die Object-Basis eines Doubles zeigt?



  • Darüber mache ich mir gedanken wenn es soweit ist. Man könnte zum Beispiel ganz einfach erstmal schauen ob es denn auch ein Double ist:

    std::wstring type = ((*it).second)->GetType().Name();
    std::wcerr << type << std::endl;
    

    Das gibt sogar Double aus ;). Jetzt aber bitte nicht vom Thema abschweifen sonst blickt hier im Thread echt niemand mehr durch.



  • Aber du hast schon verstanden, warum dein Code nicht so funktioniert, wie du es gerne hättest, ja? Was ist eigentlich die aktuelle Frage?



  • Wie kann ich einer Membervariablen einer Klasse dynamisch zur Laufzeit einen Wert zuweisen wenn ich nur den Namen bzw. Wert als String aus einer Datei ausgelesen bekomme. Das ist meine Frage.
    Mein Lösungsansatz war nun dieser, dass ich Referenzen bzw. da man diese nicht in einer Map speichern kann, Pointer auf diese zeigen lasse um dann über den Zeiger den Wert dynamisch zuzuweisen. Da wusste ich allerdings noch nicht dass das wegen Slicing nicht geht.
    Darum wollte ich jetzt wissen ob es dafür eine Lösung gibt.



  • class Object {
    public:
        virtual void Assign( const Object& other ); // oder meinetwegen operator=
    };
    
    class Double : public Object {
    public:
        void Assign( const Object& other ) {
             assert( other.GetType().Name() == "Double" ); // Oder was du zu gedenken tust
             *this = static_cast< const Double& >( other ); // oder dynamic_cast, aber dann bräuchtest du die Typnamen auch gar nicht mitschleifen
        }
    };
    


  • Es funktioniert. Danke dir für die Lösung. War auch ne schwierige Geburt ;). Ist zwar nicht 100% optimal weil jetzt jede Klasse diese Assign-Funktion mitbekommt und ich diese für jede Klasse individuell anpassen muss. Aber anders wird es wohl nicht gehen. Der Tipp mit dem überschreiben des =-Operators ist auch gut.



  • template< typename T >
    void Assign( Object& lhs, const T& rhs )
    {
    	assert( lhs.GetType() == rhs.GetType() );
    	static_cast<T&>(lhs) = rhs;
    }
    

    Ginge auch 😉 Wir, und du selbst, wissen ja noch nicht, was du am Ende nun damit machen willst 😉



  • Das wäre praktisch. Leider scheint man in templates keine Funktionen aufrufen zu können als typename. Hätte mir sonst ne Menge Schreibarbeit abgenommen.



  • Polymorphie und Wertsemantik vertragen sich nicht. Es irgendwie doch hinfrickeln zu wollen, führt garantiert zu Problemen. Ich kenne keinen einzigen Fall, in dem virtuelle Zuweisungsoperatoren sinnvoll sind.

    Wenn man Deep-Copy-Semantik möchte, ohne den dynamischen Typen zu kennen, kann man z.B. das Clone-Idiom anwenden. Dann überschreibt man gleich den (Smart-)Pointer und nicht nur das Objekt, wodurch auch Slicing verunmöglicht wird.



  • Also ganz so generell würde ich das jetzt nicht ausdrücken. Ich hab ersteres Beispiel schon erfolgreich verwenden können. Das ist nichts, worauf man ein komplettes Projekt aufbauen würde und anschließend auf Typen nicht mehr achtet, aber hat halt seine Anwendungen, bei denen zB. das reine Klonen andere Nachteile hätte.
    Ein paar Begründungen wären auch ganz nett gewesen, auch wenn ich weiß, dass man da immer sehr ausholen muss 😉 Aber so kommt das halt so rüber wie die Bibel, bei der auch jeder halbwegs intelligente hinterher fragt: Warum nur?



  • Decimad schrieb:

    Also ganz so generell würde ich das jetzt nicht ausdrücken. Ich hab ersteres Beispiel schon erfolgreich verwenden können.

    Schon möglich.
    Aber würdest Du heute noch so agieren?



  • Ich stand in der Zwischenzeit nicht wieder vor selber Problematik, daher weiß ich da jetzt auf Anhieb keine Antwort drauf! Ich denke aber mal, damals war so der Zeitpunkt, als ich von der Welt des statisch kompilierten in die Welt des dynamisch konfigurier- und parametrierbaren gegangen bin, da schießt man halt manchmal auch über das Ziel hinaus (irgendwie habe ich mir das aber damals auch einfach selber so zusammengebastelt, ohne da aus 10 Foren Hilfe zu benötigen). Inzwischen würde ich wahrscheinlich konservativer rangehen. Dafür zermartere ich mir aber jetzt vor lauter "du sollst nicht"-Regeln desöftern mal den Kopf und statt sie zu ignorieren quäle ich mich dann rum, auch wenn es bloß auf ein hinreichend funktionierendes Ergebnis ankommt 😉



  • Decimad schrieb:

    Aber so kommt das halt so rüber wie die Bibel, bei der auch jeder halbwegs intelligente hinterher fragt: Warum nur?

    Slicing wurde ja in diesem Thread schon mindestens ein Dutzend Mal erwähnt. Das ist ein allgegenwärtiges Problem bei solchem Gefrickel. Dazu kommt der umgekehrte Fall Derived = Base , wo nur das halbe Objekt überschrieben wird und undefiniertes Verhalten entstehen kann.

    Zuweisungen kann man genau dann zuverlässig verwenden, wenn man statische Typen vor sich liegen hat. Sonst muss man doch wieder Fälle unterscheiden, was den ganzen Versuch, das mit virtual unter einen Hut zu bringen, nutzlos macht. In dem Fall, wo statische Typen involviert sind, ist allerdings normale Wertsemantik ausreichend, und man kann sich die Frickelei sparen.

    Aber das Thema ist nicht neu:

    volkard schrieb:

    Ich verstehe den Bedarf gar nicht. Typverändernde Zuweisungen mache ich nicht auf Objekten, zum Beispiel weil das eh zu selten klappt, sondern auf Basisklassenzeigern, und auf einmal ist es logisch, einfach, schnell und einfach.

    Nexus schrieb:

    Nebenbei: Ein virtueller Zuweisungsoperator ist ein wenig fragwürdig. Und zwar einfach, weil C++ kein Double-Dispatching kann und der rechte Operand somit als statischer Typ interpretiert wird, was bei polymorphem Code wie

    Base* a = ...;
    Base* b = ...;
    
    *a = *b;
    

    nur die Basisklassenversion Base::operator= in Betracht zieht.

    Alternativen sind oft auch semantisch sinnvoller: Entweder, man verbietet Wertsemantik komplett (durch privaten Kopierkonstruktor und Zuweisungsoperator), oder man lässt sie auf Objekten gleichen Typs (in der gleichen Hierarchiestufe) zu, wodurch man wiederum kein virtual benötigt.

    Nexus schrieb:

    Das Problem ist, dass Wertsemantik (und damit Dinge wie Kopierkonstruktor oder Zuweisungsoperator) nicht direkt polymorph implementiert werden kann, weil die Objekte oft inkompatibel sind und man Slicing oder sonstiges undefiniertes Verhalten leichtfertig in Kauf nimmt, wenn man versucht, gleich wie bei statischen Typen vorzugehen.

    Das heisst nicht, dass polymorphe Klassen generell keine Kopierkonstruktoren und Zuweisungsoperatoren haben dürfen, jedoch sollten diese nicht virtuell sein. Beim Konstruktor ist bereits durch die Sprache gegeben, dass der dynamische Typ des neuen Objekts bekannt sein muss, beim Zuweisungsoperator jedoch nicht, was zu solchen Experimenten verleitet. Wertsemantik ist sehr wohl möglich, aber nur auf gleicher Ebene und mit statischen Typen. Für "polymorphe Wertsemantik" stellen immer noch Smart-Pointer eine Möglichkeit dar, welche z.B. eine virtuelle Clone()-Funktion aufrufen und sicherstellen, dass kein Slicing zum Tragen kommt.

    Edit: Mehr Zitate gefunden


Anmelden zum Antworten