Problem mit T& R::operator* überladen



  • Hallo,
    ich versuche einen Wrapper zu schreiben, der das gewrappte Objekt über operator * zurück gibt, aber ich scheine etwas nicht richtig zu verstehen.

    Der Code: http://codepad.org/jt8E1uol

    In Zeile 21 versuche ich das Assignment, aber scheinbar habe ich den falschen operator überladen.
    In den FAQs hier im Forum habe ich eine andere Signatur für den Operator gesehen:
    T& X::operator*() const; (http://www.c-plusplus.net/forum/232010-full 3.15)
    Was mich jedoch irritiert, ist dass ich hier doch kein T& zurückgeben kann, wenn die Methode const ist. Wenn ich nur das const am Ende hinzufüge, gibt es Probleme.
    Wenn ich die signatur in const std::string & operator() const ändere, kommt wieder das ursprüngliche Problem mit dem falschen Operator.

    Danke



  • Hm,
    ich Trottel, ich müsste zwei mal dereferenzieren, als mit **.

    Hmmm, gibt es einen Weg, wie ich die doppelte Dereferenzierung verhindern kann?



  • SWrapper * foo = new SWrapper("Test");
    

    durch

    SWrapper foo("Test");
    

    ersetzen.

    delete foo;
    

    ist dann auch nicht mehr notwendig.



  • Hallo,
    der Wrapper muss in der Programmlogik ein Pointer sein.

    Ich wundere mich gerade um meine Wissenslücke, wieso ich den Pointer Dereferenzierer so nicht aufrufen kann, sondern nur mit zwei *

    Vorher waren immer Pointer der jeweiligen Typen notwendig (string 😉
    Da ich nun aber den Schreibzugriff auf die Pointer plotten muss, wollte ich string * in einen Wrapper verpacken.
    Der Wrapper soll sich nach außen hin aber wie der unterliegende Typ verhalten, daher sollte der operator * bei der Dereferenzierung des Wrappers das gewrappte Objekte zurückgeben



  • foo ist in deinem Code ein Zeiger. Um an das Objekt zu kommen, das den überladenen operator* zur Verfügung stellt, musst du ihn dereferenzieren -- das ist der erste Stern. Dann hast du das Objekt und musst den operator* aufrufen -- das ist der zweite.

    Unter der Annahme, dass du hier einen Smartpointer baust: Es ist höchst ungewöhnlich, solche über rohe Zeiger anzusprechen. Bist du sicher, dass das so sinnvoll ist?



  • Die Situation, die ich meine ist hier zu sehen:

    http://codepad.org/ICemSz4l

    Die Zeiger, die erzeugt werden, werden an der erzeugenden Stelle behandelt (new/delete)

    Das Lesen und Schreiben soll aber gewrappt werden.
    Aber scheinbar muss ich den Use Case abwandeln und über ** dereferenzieren. Bedauerlich.

    Die naive Idee war, dass ich über den operator * transparent das gewrappte Objekte zürückgeben kann und dann regulär Zuweisungen und alle anderen Operatoren darauf anwenden kann, wie vorher.

    Edit: Ich kann natürlich operator== überladen, etc, aber für weitergabe des gewrappten Objekts an Methoden muss ich dann weiterhin Änderungen machen.



  • Seikilos schrieb:

    Die Situation, die ich meine ist hier zu sehen:

    Auch in dieser Situation ist das new und delete bei beiden Objekten unnötig. C++ ist nicht Java, man muss nicht für alles new benutzen:

    #include <string>
    
    struct SWrapper
    {
    	SWrapper(const std::string & msg)
    	{
    		member = msg;
    	}
    
    	std::string& operator*(){ return member; }
    
    	std::string member;
    };
    
    int main ()
    {
    	// Alter Fall, im System wird ein Pointer zum String erzeugt
    	std::string s1 = "Test objekt 1";
    
    	// Use case
    	if(s1 == "irgendwas")
    	{
    		// tu etwas
    	}
    
    	// Neuer Fall, im System wird ein Pointer zum Wrapper erzeugt
    	SWrapper s2 = "Test objekt 2";
    
    	// Use case sollte nicht verändert werden müssen
    	if(*s2 == "irgendwas")
    	{
    		// tu etwas
    	}
    }
    

    Wenn du Wrapper udn Strings quasi gleichwertig verwenden willst, gibts verschiedene Herangehensweisen: einen Konvertierungsoperator für den wrapper in einen string (mit den Konvertierungsoperatoren sollte man aber immer vorsichtig sein), oder aber Vergleichsmethoden mit strings/char*, je nachdem was du brauchst.



  • Ob es Pointer sind oder nicht, ist an dieser Stelle völlig irrelevant, mir geht es darum, den Use Case in beiden Fällen gleich zu halten und das ist mit Objekten auf dem Stack in deiner Version ja auch nicht passiert.

    Konvertierungsoperator gucke ich mir mal an.

    Danke



  • SWrapper * s2 = new SWrapper("Test objekt 2");
    
    // Use case sollte nicht verändert werden müssen
    if(*s2 == "irgendwas")
    {
    	// tu etwas
    }
    

    Was hast du hier vor? möchtest du den String, der im Wrapper steckt mit "irgendwas" vergleichen? Dann gibt es zwei Möglichkeiten, entweder die doppelte Referenzierung, wie du sie von vorne herein erkannt hast, also:

    SWrapper * s2 = new SWrapper("Test objekt 2");
    
    // Use case sollte nicht verändert werden müssen
    if(**s2 == "irgendwas")
    {
    	// tu etwas
    }
    

    oder den SWrapper nicht auf dem Heap anlegen (bessere Möglichkeit, wozu brauchst du ihn denn überhaupt dort?)

    SWrapper s2("Test objekt 2");
    
    // Use case sollte nicht verändert werden müssen
    if(*s2 == "irgendwas")
    {
    	// tu etwas
    }
    

    Das hat zur volge, dass du nicht mehr delete s2 aufrufen musst. Ich würde dir empfehlen, guck dir mal an, was Wann das Schlüsselwort new eigentlich Sinn macht, es macht momentan den Eindruck, als ob du von Java kommst und es immer benutzt.

    PS.: ließ doch einfach mal, was hier gepostet wird, pumuckl hat die Frage beantwortet, die du danach gestellt hast 😉

    Edit:
    Neben Konvertierungsmöglichkeiten (welche beim Wrapper sogar noch Sinn machen) gibt es auch die Möglichkeiten Vergleichsoperatoren zu überladen (==, !=, < etc.). wenn du nun

    bool operator==(const SWrapper& lhs, const std::string& rhs);
    bool operator==(const std::string& lhs, const SWrapper& rhs);
    

    überlädst, kannst du auch den Wrapper mit einem String vergleichen. Wie schön die Lösung ist, ist ne andere Sache 😉


Anmelden zum Antworten