Frage zu Konstruktor aus Buch Beispiel



  • Morgen, wofür steht hier .year();

    Date::Date() :
         y(default_date(). year() )
    {
    
    }
    

    Soweit hab ich das Beispiel verstanden:

    Zuerst wird

    Date::Date()
    

    aufgerufen danach

    default_date()
    

    und default_date() wiederrum ruft den Konstruktor

    Date::Date(int yy, Month mm, int dd) :
         y(yy), m(mm), d(dd)
    

    auf hier werden dann die privaten Variablen initialisiert.
    Danach lande ich wieder

    default_date()
    

    Konstruktor und dann wird ein Statisches Konstantes Objekt zurückgeben? Wofür eigentlich wer erhält das dann?
    [Wenn ich das return dd; entferne funktioniert das Programm trotzdem]

    Jetzt befinde ich mich wieder im Konstrutkro

    Date::Date()
    

    und jetzt wird die Funktion

    int year() const
    

    aufgerufen diese Funktion gibt die Variable y wieder wer erhält den Wert der Variable y jetzt an der Stelle?

    Im Buch wird die Stelle leider nicht wirklich ausführlich erklärt ...

    //
    // This is example code from Chapter 9.7.3 "Default constructors" of
    // "Programming -- Principles and Practice Using C++" by Bjarne Stroustrup
    //
    
    #include <iostream>
    #include <vector>
    using namespace std;
    
    //------------------------------------------------------------------------------
    
    class Date {
    public:
        enum Month {
            jan=1, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec
        };
    
        Date();                         // default constructor
    
        Date(int yy, Month mm, int dd); // constructor
    
        int   year() const
        {
            cout<<"year aufgerufen"<<endl;
            return y;
        }
    
    private:
        int   y;
        Month m;
        int   d;
    };
    
    //------------------------------------------------------------------------------
    
    const Date& default_date()
    {
        static const Date dd(2001,Date::jan,1);
        return dd;
    }
    
    //------------------------------------------------------------------------------
    
    Date::Date() :
    
         y(default_date(). year() )
    {
    
    }
    
    //------------------------------------------------------------------------------
    
    Date::Date(int yy, Month mm, int dd) :
         y(yy), m(mm), d(dd)
    {
    }
    
    //------------------------------------------------------------------------------
    
    int main()
    {
        vector<Date> birthdays(10);
    
      cout<<"Ausgabe: " << birthdays[0].year();
    
        /*/
        // Without the default constructor, we would have had to be explicit:
        {
            vector<Date> birthdays(10,default_date());
        }
        /*/
    
    system("PAUSE");
    }
    


  • Bjarne schrieb:

    Morgen, wofür steht hier .year();

    Date::Date() :
         y(default_date(). year() )
    {
    
    }
    

    Du solltest hier lieber kein Leerzeichen setzen. y wird mit dem Ergebnis des Ausdrucks "default_date().year()" initialisiert.

    default_date ist eine globale Funktion, die einmalig ein Date-Objekt mit dem Datum 1.1.2001 erzeugt. Anschließend wird die Methode year() des Objektes aufgerufen, die einen Integerwert (das Jahr, also 2001) zurückliefert.



  • 2 Frage habe ich noch:

    Wo ist der Sinn hier nochmal year() im Konstruktor aufzurufen?

    Date::Date() : y(default_date(). year() )/cpp]
    
    Ich mein die Variable y wird doch schon in diesem Konstruktor initialisiert:
    [cpp]Date::Date(int yy, Month mm, int dd) : y(yy), m(mm), d(dd)
    

    Und wo ist der Sinn hier nochmal return dd; zu schreiben? Wenn ich das weglasse dann funktioniert das Programm auch.

    const Date& default_date()
    {
        static const Date dd(2001,Date::jan,1);
        return dd;
    }
    


  • default_date() ist eine statische Methode, die dir ein Datum ("01.01.2001") erstellt.

    year() ist eine nicht-statische Methode, die dir das Jahr der Instanz zurückgibt, auf dem diese Methode aufgerufen wurde.

    und "return dd" bedeutet, dass der Wert der Variable "dd" als Rückgabewert verwendet wird. Wenn dein Programm auch ohne diesen Aufruf funktioniert, dann gibt es mehrere Möglichkeiten:

    1. Dein Compiler ist extrem tolerant
    2. dein Compiler hat eine gut funktionierende Glaskugel
    3. dein Programm funktioniert nur zufällig


  • Mod

    Bjarne schrieb:

    2 Frage habe ich noch:

    Wo ist der Sinn hier nochmal year() im Konstruktor aufzurufen?

    Date::Date() : y(default_date(). year() )/cpp]
    
    Ich mein die Variable y wird doch schon in diesem Konstruktor initialisiert:
    [cpp]Date::Date(int yy, Month mm, int dd) : y(yy), m(mm), d(dd)
    

    Die beiden Konstruktoren sind zwei völlig unterschiedliche funktionen, es wird nicht zuerst der eine, dann der andere aufgerufen, sondern genau einer von beiden. (In C++11 kann man auch erreichen, dass der eine Konstruktor den anderen aufruft, aber das passiert auch dort nicht von alleine, sondern nur auf Anweisung des Programmierers)[quote]

    Und wo ist der Sinn hier nochmal return dd; zu schreiben? Wenn ich das weglasse dann funktioniert das Programm auch.

    Über das Ende einer Funktion mit Rückgabewert zu laufen, ohne etwas zurück zu geben, erzeugt (ausgenommen in Funktion main) undefiniertes Verhalten! Mach das nie! Du hast vielleicht gerade Glück, dass in den Registern deiner Maschine gerade noch die richtigen Verweise auf das Objekt stehen, die zu den calling conventions deines Systems passen, aber wenn du auch nur irgendwo die kleinste Kleinigkeit änderst, kann da auch was ganz anderes stehen und wer weiß was passieren. Und wenn du gerade kein Wort von den Registern und conventions verstanden hast, dann zeigt dir das auch, dass da komplizierte Sachen passieren und du einfach nur wahnsinnig Glück hast, dass es funktioniert (oder Pech, da du dadurch diesen klaren Fehler nicht findest). Dein Compiler sollte dich auch davor warnen können, versuch mal rauszufinden, wie man die Compilerwarnungen aktiviert. Dadurch kann man sehr viele Fehler gleich vom Compiler finden lassen, die dieser wegen irgendwelcher Regeln nicht als Fehler nennen darf, die aber fast immer falsch sind. Warnungen sollte man immer angeschaltet haben.



  • Bjarne schrieb:

    2 Frage habe ich noch:

    Wo ist der Sinn hier nochmal year() im Konstruktor aufzurufen?

    Ich verstehe deine Fragen nicht wirklich. Bzw. glaube ich das du hier ein Missverständnis hast, was Objekte sind.

    Wenn ich ein Date-Objekt erzeuge, hat dies seine eigenen Werte. Sprich die Membervariable y von zwei unterschiedlichen Date-Objekten sind nicht identisch und können unterschiedliche Werte haben.

    ...
    Date a(2001, jan, 1); // a.y ist 2001
    Date b(2003, feb, 2); // b.y ist 2003
    ...
    

    Ein Konstruktor dient nun dazu ein Objekt zu initialisieren, und hier holst du dir Standardwerte aus einem anderen Objekt. 'y' ist nicht gleich 'y'.

    Bjarne schrieb:

    Und wo ist der Sinn hier nochmal return dd; zu schreiben? Wenn ich das weglasse dann funktioniert das Programm auch.

    Ohne das return kann dein Programm nicht funktionieren, es sei den du hast noch etwas anderes geändert.

    const Date& default_date()
    {
        static const Date dd(2001,Date::jan,1);
        //return dd;
    }
    

    Kann nicht kompilieren, oder dein Compiler ist extrem Fehlerhaft...


  • Mod

    asc schrieb:

    Kann nicht kompilieren, oder dein Compiler ist extrem Fehlerhaft...

    Welcher Compiler kompiliert das nicht? Dafür müsste der Compiler beweisen können, dass der Programmablauf hier unter gar keinen Umständen korrekt sein kann. Das wäre im Allgemeinen sehr schwierig. Außerdem ist das Programm laut Standard nicht ill-formed, sondern zeigt bloß undefined behaviour.



  • SeppJ schrieb:

    asc schrieb:

    Kann nicht kompilieren, oder dein Compiler ist extrem Fehlerhaft...

    Welcher Compiler kompiliert das nicht?

    Okay, sagen wir es anders: Die meisten werden wenigstens eine Warnung (je nach Warnstufe, und die sollte man eher hoch einstellen eher einen Fehler) ausgeben bei einem fehlenden return (oder throw). Das war das erste was mich am C++ Builder aufgeregt hat, das der nur eine Warnung ausgibt.



  • Ich glaub ich hab dank euch meinen Fehler erkannt.

    Date::Date() : y(default_date(). year() )
    

    Ich merk mir das jetzt so:
    default_date() gibt ein Objekt zurück.

    Dann habe ich: objekt . year();

    year(); gibt einen Wert zurück.

    Jetzt habe ich: objekt.wert

    y(default_date() . year() ) <-- Der Punkt darin hat mich iritieert

    [quote="SeppJ"]Die beiden Konstruktoren sind zwei völlig unterschiedliche funktionen, es wird nicht zuerst der eine, dann der andere aufgerufen, sondern genau einer von beiden. (In C++11 kann man auch erreichen, dass der eine Konstruktor den anderen aufruft, aber das passiert auch dort nicht von alleine, sondern nur auf Anweisung des Programmierers)

    Aber bei mir werden alle Konstruktoren aufgerufen und nicht nur einer von beiden. Ich hab cout<<"ok"; in jeden Konstruktor geschrieben und es wurde jedesmal ausgeben.



  • Bjarne schrieb:

    Aber bei mir werden alle Konstruktoren aufgerufen und nicht nur einer von beiden. Ich hab cout<<"ok"; in jeden Konstruktor geschrieben und es wurde jedesmal ausgeben.

    du hast aber auch zwei Instanzen. Jede Instanz wird von genau einem Konstruktor erstellt.



  • Bjarne schrieb:

    Ich glaub ich hab dank euch meinen Fehler erkannt.

    Fast, ein paar Verständnisprobleme hast du noch.

    Bjarne schrieb:

    y(default_date() . year() ) <-- Der Punkt darin hat mich iritieert

    Aus diesem Grund hatte ich auch anfangs geschrieben, das ich das Leerzeichen weg lassen würde. Der Punkt bedeutet im wesentlichen das du auf etwas des vorherigen zugreifst (ob dies nun ein Objekt, oder das Ergebnis einer Funktion ist). Für mich stellt dies deshalb eine Einheit dar, das Leerzeichen führt bei mir zu einer optischen Trennung beider Ausdrücke.

    ...
    Date a(2001,jan,1);
    std::cout << a.year(); // Methode year des Objektes a aufrufen.
    ...
    

    Dies ist aber Geschmackssache.

    Bjarne schrieb:

    Aber bei mir werden alle Konstruktoren aufgerufen und nicht nur einer von beiden. Ich hab cout<<"ok"; in jeden Konstruktor geschrieben und es wurde jedesmal ausgeben.

    Hieraus ziehst du aber falsche Schlüsse. Wenn du die Methode "default_date" wie im Buch verwendest solltest du mal den Test wie folgt wiederholen: Lege mal anschließend mehrere Objekte an. Du wird im folgenden Code 4 Konstruktoraufrufe sehen:

    ...
    Date a; // 1 + 2 (Dazu unten mehr)
    Date b; // 3
    Date c; // 4
    ...
    

    Schauen wir uns nochmal die Methode "default_date" an.

    const Date& default_date()
    {
        static const Date dd(2001,Date::jan,1);
        return dd;
    }
    

    Was genau geschieht in dieser Methode?

    Es wird hier logischerweise auch ein Konstruktor aufgerufen, da "dd" auch ein Date-Objekt ist. Somit hast du beim Erzeugen eines Date-Objektes über den Standardkonstruktor effektiv 2 verschiedene Objekte. Das static bedeutet gleichzeitig das diese Variable nur einmal beim ersten Aufruf erzeugt wird, jeder weitere Aufruf greift auf dieses Objekt zu (es wird erst bei Programmende gelöscht).

    Somit wird jeder weitere Aufruf des Standardkonstruktors wiederum nur einen Konstruktoraufruf erzeugen, da "dd" bereits existiert.


Anmelden zum Antworten