Wann wird das Objekt zerstört? Und noch eine Stilfrage



  • Hi

    Ich versuche mich gerade an einer polyalphabetischen Verschlüsselung. Ich habe in meinem Programm eine Funktion zum Verschlüsseln, bei der ich nicht ganz sicher bin, ob ich den string mit new erzeugen muss, wenn ich ihn als Referenz zurückgebe.

    Im Moment sieht meine Funktion so aus:

    std::string & encrypt( const std::string & text_to_encrypt )
    	{
    
    		std::string * encrypted_text = new std::string;
    
    		// Miscellaneous
    
    		return *encrypted_text;
    	}
    

    Hier bin ich mir sicher, dass encrypted_text auch nach Beendigung der Funktion bestehen bleibt.

    Meine Frage ist nun, ob es auch ohne new gehen würde, so:

    std::string & encrypt( const std::string & text_to_encrypt )
    	{
    
    		std::string * encrypted_text;
    
    		// Miscellaneous
    
    		return *encrypted_text;
    	}
    

    Meiner Meinung nach geht das nicht weil die Variable gelöscht wird und man eine Referenz auf den gelöschten Speicher hat ==> undefined behavior.

    Dann habe ich noch eine kleine Stilfrage:
    Wenn ihr über einen Funktionsaufruf z.B. den Schlüssel für die Verschlüsselung bekommen wollt. Welche Funktionsdeklaration bevorzugt ihr oder was ist das übliche in C++?

    void get_encryption_key( std::string & key );
    
    std::string & get_encryption_key();
    


  • Deine Referenzen bringen gar nix. Du holst dir da nen richtig schönen MemoryLeak...

    std::string encrypt( const std::string & text_to_encrypt )
        {
    
            std::string encrypted_text;
    
            // Miscellaneous
    
            return encrypted_text;
        }
    

    Und gut ist. Hast zwar eine Kopie, aber dafür bist du safe unterwegs.
    Wenn du unbedingt diese Kopie vermeiden willst, dann gib gleich den Pointer zurück, und schreib bei der Doku dazu "the user is responsible for destruction".
    Die zweite Variante ist genau die gleiche wie die erste, nur dass du den Pointer nicht initialisierst...

    Zur Stilfrage:
    wenn du nen getter hast, gib doch was zurück. Die Variante "return über Parameter" nimmt man hauptsächlich dann, wenn man mehrere return values hat (soll schonmal vorkommen).



  • Da encrypted_text ein Zeiger im automatischen Speicher ist, bleibt der natürlich NICHT nach Beendigung der Funktion weiter bestehen. Aber das, worauf dieser Zeiger zeigt(e), hast du ja selbst per new angelegt und muss daher auch irgendwann per delete wieder gelöscht werden.

    Ohne new geht es natürlich auch und so solltest Du es machen:

    string encrypt(string in)
    {
      string out;
      out.swap(in);
      // out in-place verändern
      return out;
    }
    

    (dies kann ganz besonders gut von modernen Compilern optimiert werden, so dass kein unnötiges Kopieren anfällt).

    Antwort zur Stilfrage:
    DRITTENS. Nämlich Rückgabe von std::string (keine Referenz, kein Zeiger!)

    kk



  • die meisten compiler sind auch in der lage, die unnötige kopie wegzuoptimieren - also über die geschwindigkeit solltest du dir da keine gedanken machen.

    bb



  • icarus2 schrieb:

    Meiner Meinung nach geht das nicht weil die Variable gelöscht wird und man eine Referenz auf den gelöschten Speicher hat ==> undefined behavior.

    Das hängt davon ab, was in // Miscellanous passiert. Du darfst ohnehin keinen nicht initialisierten Zeiger dereferenzieren, daher müsstest du ihn entweder auf den Parameter zeigen lassen oder dynamisch Speicher anfordern. In dem Falle wäre es okay. Aber warum benutzt du nicht den automatischen Speicher, also sowas?

    std::string encrypted_text;
    

    icarus2 schrieb:

    Wenn ihr über einen Funktionsaufruf z.B. den Schlüssel für die Verschlüsselung bekommen wollt. Welche Funktionsdeklaration bevorzugt ihr oder was ist das übliche in C++?

    Keins von beiden, sondern so:

    std::string get_encryption_key();
    

    Und wenn dein Compiler kein RVO kann und die Kopie wirklich ein Problem darstellen sollte, kannst du immer noch die Version mit dem Output-Parameter oder

    std::auto_ptr<std::string> get_encryption_key();
    

    nehmen.

    Allerdings würde ich eine Methode, die nichts zurückgibt, nicht "get" nennen. Der Output-Parameter macht ausserdem die Verwendung in anderen Ausdrücken komplizierter.

    save_string(get_encryption_key());
    

    vs.

    std::string key;
    compute_encryption_key(key);
    save_string(key);
    


  • Jap, ich glaube ich werde die Kopie nehmen. Ist sicherer.

    Mir ist das mit dem Rückgabetyp auch lieber. Werd ich ab jetzt so machen.

    Danke für eure Antworten!


Anmelden zum Antworten