Map von Pointern auf Membervariablen



  • Hi, ich komme seit Stunden mit folgendem Problem nicht mehr weiter. Und zwar habe ich mir gedacht ich lege eine std::map von Referencen an, mit denen ich dann über einen Key auf die Membervariablen der Klasse zugreifen kann. Leider will C++ grundsätzlich nicht so wie ich das möchte ;). Also habe ich mir gedacht nehme ich halt einfach Pointer anstatt Referenzen, weil Referenzen in einer map nicht hinzugefügt werden können. Hier der Code:

    Double width = 320;
    std::map<std::wstring, Object*> map;
    map.insert(std::pair<std::wstring, Object*>(L"width", &width));
    std::wcout << L"Width before: '" << width << L"'" << std::endl;
    map[L"width"] = &Double(640);
    std::wcout << L"Width after: '" << width << L"'" << std::endl;
    
    //Ausgabe:
    //Width before: '320'
    //Width after: '320'
    

    "Double" ist von "Object" abgeleitet. Wer kann mir sagen warum "width" nicht geändert wird? Ich denke ich lasse den Zeiger jetzt einfach nur auf ein neues Doubleobject zeigen, anstatt wie gewollt das Object auf den der Zeiger weist zu ändern.


  • Mod

    Du änderst einen Zeiger. Das ändert do nicht das Objekt auf das gezeigt wird. Wenn man das ganze gemappe weglässt, machst du dies:

    int foo=2;
    cout<< foo; // 2
    int *pfoo=&foo;
    int bar=4;
    pfoo=&bar;
    cout<<foo;  // 2
    


  • &Double(640);
    

    RValue-Objekte sind nicht adressierbar. Der Zeiger wäre am Ende der Anweisung ohnehin ungültig.



  • Soll das heißen mit einer map geht das nicht? Folgendes funktioniert:

    Double d = 320;
    Double *e = &d;
    *e = 640;
    

    Dementsprechend habe ich den Code in

    Double d = 320;
    std::map<std::wstring, Object*> map;
    map.insert(std::pair<std::wstring, Object*>(L"d", &d));
    std::wcerr << L"d before: '" << d << L"'" << std::endl;
    *map[L"d"] = Double(640);
    std::wcerr << L"d after: '" << d << L"'" << std::endl;
    

    geändert. Aber funktioniert leider nicht.


  • Mod

    Das hat nichts mit map zu tun, das hat was mit deinen Zeigern zu tun. Du dereferenzierst da einen Object* und weist ihm dann einen Double zu. Überleg dir mal, welche Typen die Objekte da haben und was da wohl passiert.

    Falls du nicht selber drauf kommst:
    Google: c++ slicing
    Aber versuch das lieber erst einmal selber zu verstehen, dann kannst du es dir besser merken.



  • Ok, ich dachte bei Pointern passiert das nicht. Mit Referenzen würde es aber gehen, bringt mir aber auch nichts.
    Gibt es denn keine Möglichkeit dass ich eine Liste/Map von Pointern/Referenzen anlege die auf die Membervariablen der Klasse zeigen, aber so dass ich den Wert dann auch noch ändern kann?



  • Doch, aber du darfst die Objekte nicht slicen. Lass die Zeiger auf das abgeleitete Objekt zeigen und versuch nicht, letzteres in ein Basisobjekt zu kopieren.



  • Macht der Code nicht genau das?

    Double d = 320;
    Object *e = &d;
    *e = Double(640);
    

    Der Zeiger "e" zeigt auf die Variable "d" der abgeleiteten Klasse "Double". Oder meinst du dass ich anstatt

    Object *e = &d;
    
    Double *e = &d;
    

    schreiben soll?
    Dann kann ich aber in der map nur noch Double speichern und keine Ints, Strings usw. Mir ist auch nicht klar warum der Computer überhaupt den Double in ein Object wandeln möchte. Der soll einfach nur zu der Adresse des Double d springen und dort den Wert Double(640) schreiben. Mehr nicht ;).

    Wären evtl. virtuelle Funktionen hier brauchbar? Mal angenommen alle Klassen würden von Object die Funktion

    virtuell void Parse(Object &object)
    

    erben. In der Double Klasse hätte ich dann sowas wie

    virtuell void Parse(Double &double)
    

    . Könnte ich dann

    Double d = 320;
    Object *e = &d;
    *e->Parse(Double(640));
    

    aufrufen?



  • Student83 schrieb:

    Macht der Code nicht genau das?

    Sagen wir ja: Nein.

    Du dereferenzierst den Zeiger und weist ihm das Objekt zu, anstatt den Zeiger selbst neu zu setzen. Schau dir vielleicht nochmals die Semantik der Dereferenzierung genau an.

    Student83 schrieb:

    Wären evtl. virtuelle Funktionen hier brauchbar?

    Kommt drauf an, was du willst. Aber virtuelle Funktionen müssen die gleiche Signatur haben (kovariante Parameter sind nicht möglich). In meinen Augen wäre eine virtuelle Funktion ohne Parameter sinnvoller (wenn überhaupt). Ausserdem versuchst du, RValues an Referenzen auf nicht-konstante Objekte zu binden, was verboten ist. Und nicht zuletzt heisst es virtual ...



  • Evtl. verstehen wir uns falsch. Genau das will ich doch.

    Ein Pointer referenziert ein Objekt. Wenn du vom Pointer wieder zum Objekt willst, musst du den Pointer "dereferenzieren".

    Die Pointer werden beim Applikationsstart auf die Membervariablen referenziert, zeigen also auf sie. Und das soll sich die ganze Lebensdauer des Programms über nicht mehr ändern. Ich möchte die Werte der Member auf die gezeigt wird verändern, muss also den Zeiger dafür dereferenzieren.
    Nur das klappt nicht da wie ihr schon gesagt habt hier Slicing passiert.
    Ich habe also meine Klasse

    class Control
    {
    private:
        Double width;
        Double height;
    };
    
    Control::Control()
    {
        Object* zeigerAufWidth = &width;
        Object* zeigerAufHeight = &height;
        //Irgendwie dem Member width einen Wert zuweisen (über den Pointer)
        //Irgendwie dem Member height einen Wert zuweisen (über den Pointer)
    }
    

    Was mir fehlt ist die Möglichkeit, wie ich über die Zeiger den Membern width und height einen anderen Wert übergebe.



  • Das sind absolute Grundlagen. Lies dir diesen Artikel zu Zeigern durch.



  • Ok, bringt mich aber nicht weiter. Du siehst das Problem nicht. Das Problem ist das Slicing.
    Dies hier geht:

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

    Aber nur wenn man vorher weis dass man Double hat. Ich erstelle die Objecte zur Laufzeit und weis nur dass sie die gemeinsame Basisklasse Object haben.
    Darum sieht mein Code so aus:

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

    Mit der Folge dass Slicing auftritt. Wie kann ich dieses Slicing verhindern/umgehen? Das ist meine Frage.



  • Oh man ich glaubs nicht. Ich musste *e nur in Klammern setzen.

    (*e) = Double(640);
    

    Jetzt geht es. Hat das irgend einen Grund?



  • Das ist definitiv nicht der Fehler, da das in diesem Fall egal ist.



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


Log in to reply