Konstruktoraufruf bei temporären Objekten



  • Aloha Community!

    Ich hab mal wieder (hoffentlich zumindest nur für mich) ein haarzerraufendes Problem: Ich habe eine Klasse User mit einem Konstruktor, der als Parameter eine konstante l-value Referenz vom Typ std::string entgegen nimmt. In diesem Konstruktor wird (falls es den User noch nicht gibt) das Passwort für den User eingegeben und eine Datei für ihn angelegt. Wenn es den User schon gibt, passiert noch nichts erklärungswertes. Dies sieht dann so aus:

    User::User(const std::string& name) : user_name(name){
    
        if(fs::exists("users") == false)
            fs::create_directory("users");
    
        if(fs::exists("users/" + user_name) == false){
            password = create_password(user_name);
            write();
        }
    }
    

    Um nun einen User zu erstellen brauchen wir eigentlich nur noch ein temporäres Objekt, z.B. so:

    User("<InsertNameHere>");
    

    Nun komme ich mal zur Frage: Wann würde bei so einer Zeile ( User("<InsertNameHere>"); oder User(myString); ) nicht der Konstruktor aufgerufen werden? Ich weiß, dies ist nun vmtl. eine recht dumme und noobige Frage, aber diese Frage stelle ich auch nicht ohne jeglichen Grund. Also meiner Meinung nach sollte so eine Zeile wie MyClass(myParams); ganz einfach nur den Konstruktor und schlussendlich wieder den Destruktor der angegebenen Klasse aufrufen.

    Jedoch... Man nehme folgenden Code:

    // create a new user
    void create_user(){
        std::string user_name;
    
        do{
            ...
    
        } while(user_name.empty() && user_name != "0");
    
        if(user_name != "0")
            User(user_name);
    }
    

    In der while-Schleife wird einfach nur einen Namen eingegeben, und wenn man nicht grad "0" (steht für "Go back") eingegeben hat, wird ein temporäres Objekt des Typs "User" erstellt, und der Konstruktor, den ich oben gepostet habe sollte aufgerufen werden.

    Tut er aber nicht.

    Nur wenn ich die Zeile...

    if(user_name != "0")
            User(user_name);
    

    ...durch...

    if(user_name != "0")
            User eigentlichBraeuchteIchNurEinTemporaeresObjekt(user_name);
    

    ...oder durch...

    if(user_name != "0")
            User(user_name.c_str());
    

    ...ersetze funktionerts, sprich, der Konstruktor wird aufgerufen.

    Aber ich versteh grad echt nicht, warum bei User(user_name); echt kein Konstruktor aufgerufen wird, und bei allen anderen Varianten schon? Wie kann das überhaupt möglich sein!? Ich weiß, so wie mein Konstruktor aussieht würde der sich nicht bemerkbar machen, falls es den User schon gibt, aber ich habs etliche Male versucht - ob der User existiert oder nicht - der Konstruktor wird bei der ersten Variante einfach nicht aufgerufen.

    Weiß mir zufälligerweise jemand zu helfen?
    Falls jemand weitere Informationen zu dem Code bräuchte müsste nur Alarm schlagen, und schon kämen die Zeilen angeflogen.

    LG



  • <InsertNameHere> schrieb:

    und der Konstruktor, den ich oben gepostet habe sollte aufgerufen werden.

    Tut er aber nicht.

    wie stellst du fest, dass der Konstruktor nicht aufgerufen wird? Hast du noch andere Konstruktoren?

    Funktioniert das hier:

    user_name = "test";
    if(user_name != "0")
            User(user_name);
    


  • Wenn es dir nur darum geht einen User anzulegen, warum erstellst du dafür keine eigene Funktion, die genau das macht?

    Alternativ kannst du dir auch ein Macro/Funktion bauen, die ein temporäres Objekt anlegt:

    void CreateUser(const std::string& name)
    {
     User temp(name);
    }
    

    bzw.

    #define CREATE_USER(x) do { User tmp__(x); } while(0)
    

    Ansonsten hast du auch bei:

    if(user_name != "0")
            User eigentlichBraeuchteIchNurEinTemporaeresObjekt(user_name);
    

    ein temporäres Objekt. Nach dem Verlassen des Blocks, wird das Objekt wieder zerstört.



  • if(user_name != "0")
             User(user_name);
    

    Wird hier vielleicht im if-scope eine neue Variable user_name definiert und der Standardkonstruktor von User aufgerufen?



  • Probier mal folgendes:

    User( std::move(user_name );
    


  • wie stellst du fest, dass der Konstruktor nicht aufgerufen wird? Hast du noch andere Konstruktoren?

    Dies stelle ich fest, indem ich in die erste Zeile des Konstruktors eine Textausgabe mit std::cout einbaue, wobei dieser Text nicht ausgegeben wird.
    Die Klasse enthält nur einen Default-Ctor, sowie den Ctor, den ich oben gepostet habe.

    Und ja, wenn ich eine Texteingabe vor dem temporären Objekt einbaue, so wird diese auch angezeigt, Beispiel:

    if(user_name != "0"){
            std::cout << "Der Name: " << user_name;
            User(user_name);
    }
    

    Funktioniert das hier:

    user_name = "test";
    if(user_name != "0")
            User(user_name);
    

    Nein, tut es nicht.

    Ansonsten hast du auch bei:

    if(user_name != "0")
            User eigentlichBraeuchteIchNurEinTemporaeresObjekt(user_name);
    

    ein temporäres Objekt. Nach dem Verlassen des Blocks, wird das Objekt wieder zerstört.

    Ist klar, aber auch wenn es nur eine Minioptimierung wäre möchte ich nicht Ressourcen für den Namen verschwenden, wobei es doch auch ohne funktionieren sollte.



  • Teste bitte mal std::move (s. Beitrag oben).



  • realistic steak schrieb:

    if(user_name != "0")
             User(user_name);
    

    Wird hier vielleicht im if-scope eine neue Variable user_name definiert und der Standardkonstruktor von User aufgerufen?

    Nein.

    Probier mal folgendes:

    User( std::move(user_name ));
    

    Mit Move Semantics funktionert es! :0
    Aber warum funktionierts nur somit? Das ist mir grad schleierhaft.



  • User(user_name);
    

    ist syntaktisch dasselbe wie

    User user_name;
    

    Du hast also wahrscheinlich noch einen Konstrukor ohne Argumente, der dort aufgerufen wird. Dass es mit std::move funktioniert hat also mit Move-Semantik nichts zu tun.



  • Wieso gehst du da eigentlich nicht mit einem Debugger Schritt für Schirtt durch?
    Dann siehst du ja was passiert...



  • Bashar schrieb:

    User(user_name);
    

    ist syntaktisch dasselbe wie

    User user_name;
    

    Soso! Das ist mir nun aber neu! Wieder was gelernt 😉

    Du hast also wahrscheinlich noch einen Konstrukor ohne Argumente, [...]

    Exakt.

    Wieso gehst du da eigentlich nicht mit einem Debugger Schritt für Schirtt durch?
    Dann siehst du ja was passiert...

    Stimmt, da hast du auf jeden Fall Recht, und ich wäre vmtl. auch selber drauf gekommen. Dachte halt anfangs, dass ich ihn für so ein kleines Ding nicht brauchen würde und 1 - 2 Textausgaben reichen würden.
    Tjo...
    So wars aber nicht! 😃
    Ich nehm mir Deine Worte auf jeden Fall zu Herzen.

    Großes Dankeschön an alle Helfer. 🙂



  • Bin ich eigentlich der Einzige, der findet, dass man das so nicht lösen sollte?

    Syntaktisch würde man hier als Anwendungsprogrammierer nicht erwarten, dass User(xy); überhaupt irgendeinen Effekt hat. Ich würde die Persistenzschicht von der Arbeitsdaten-Schicht der Applikation trennen. Braucht man auch dann, wenn man in irgendeinem Dialog einen Benutzer anlegt und auf Abbrechen klickt, was dann? Diese Lösung hier wird sich wohl bei jeder Vergrößerung des Projekts als unsinnig herausstellen.



  • Eisflamme schrieb:

    Syntaktisch würde man hier als Anwendungsprogrammierer nicht erwarten, dass User(xy); überhaupt irgendeinen Effekt hat.

    std::ofstream("gegenbeispiel.txt");
    

    @<InsertNameHere>: Wenn du schon std::move (C++11) hast:

    std::string beweis = "gegenbeispiel.txt";
    std::ofstream{beweis};
    


  • Btw, eigentlich wären C++11-Tags äusserst praktisch nicht jedesmal im Satz C++11 erwähnen zu müssen.

    [code="cpp11"]
    // C++11-Code
    [/code]



  • Anwendungsprogrammierer schrieb:

    Btw, eigentlich wären C++11-Tags äusserst praktisch nicht jedesmal im Satz C++11 erwähnen zu müssen.

    [code="cpp11"]
    // C++11-Code
    [/code]

    👍
    Hab das mal in "Forentechnik" vorgeschlagen.



  • Anwendungsprogrammierer schrieb:

    Eisflamme schrieb:

    Syntaktisch würde man hier als Anwendungsprogrammierer nicht erwarten, dass User(xy); überhaupt irgendeinen Effekt hat.

    std::ofstream("gegenbeispiel.txt");
    

    Moment, ich habe nicht gesagt, dass das bei jeder Klasse so ist. 😉

    Aber ich erwarte bei einer User-Klasse nicht ein automatisches Mapping aufs Dateisystem. Gut, man kann das natürlich dokumentieren, aber es ist zumindest Mal unüblich, dass ein Objekt zur Datenhaltung (nicht ein Objekt zur Dateiausgabe...) automatisch gespeichert wird.

    Aber gut, muss OP wissen, wollte ich nur zu bedenken geben.


Anmelden zum Antworten