Probleme mit Kopierkonstruktoren



  • @NES-Spieler

    Wieso gehen solche Sachen nicht innerhalb ein und derselben Klasse?

    Das beisst sich irgendwie mit der Philosophie der Konstruktoren.

    Wenn du aber nu mitten aus einem konstruktor in nem anderen springst, wie soll der andere konstruktor wissen das dein 1. konstruktor schon mal mit der konstruktion angefangen hat ^^ siehst du die probleme die es geben koennte wenn es ohne weiteres möglich waer ? bei ner member kannst du von festen vorbedingungen ausgehen, nen konstruktor muss immer von null aus starten.

    Und wenn du gleiche funktionalitaeten in unterschiedlichen konstructoren hasst, lagere die wirklich in private methoden aus, die stoeren doch keinen (inline sind die sogar kostenlos, performancetechnisch)... machen eigentlich nur uebersichtlicher ....

    Noch was, konstruktoren sind nich nur praktisch, sondern auch gefaehrlich ^^ klingt komisch, isss es aber auch 🙂
    nen konstruktor ist gleichzeitig immer auch ne umwandlung (wenn er nich mit expliziet deklariert wurde) damit macht man den compiler bei zig kosntruktoren echt schwer, den richtigen zu finden ^^ und man bekommt teilweisse tolle effekte die maan nicht will ^^
    Also lieber 1 konstruktor weniger als einen zu viel !

    Und da du dich mit Konstruktoren grad beschaeftigst, Goldene Regel:
    lagere so viel als möglich in die initialisierungslisten aus. Nutze notfalls sogar statische member zu ^^
    vielleicht bringt dich das dem Wesen eines konstruktors etwas naeher (konstruktoren sollten auch schnell sein, und logische fehlerbehandlung in konstruktoren sollt man vermeiden, die sollten nur "knallen" (exception) wenn es kritische probleme gibt.)

    Ciao ...



  • RHBaum schrieb:

    Goldene Regel:
    lagere so viel als möglich in die initialisierungslisten aus. Nutze notfalls sogar statische member zu ^^

    Jetzt will ich aber auch noch wissen warum 🙂

    Ich weis zwar das man Initialisierungslisten verwenden soll aber ich kann mir nicht erklären warum.
    Und was meinst du mit "nutze notfalls statische Member". Meinst du damit als static deklarierte Membervariablen? Wie können die die Konstruktion beschleunigen?



  • Ich danke Euch allen für Eure Antworten. War recht informativ.



  • Ich weis zwar das man Initialisierungslisten verwenden soll aber ich kann mir nicht erklären warum.

    Mir hat man das mal so erklärt: Wenn man keine Initialisierungsliste hat, werden die Werte unter Umständen doppelt belegt:

    Konstruktor ()
    //Hier werden die Membervariablen mit einem nicht definierten Wert belegt.
    {
        a=1;
        b=2;
        //Und hier werden sie nochmal belegt.
    }
    

    Dagegen:

    Konstruktor ():
        a (1), b (2)
        /*Die Membervariablen erhalten sofort den richtigen Wert
          und werden bloß einmal belegt.*/
    {
    }
    

    Ich stelle mir das wie den Unterschied zwischen

    int i;
    i=0;
    

    und

    int i=0;
    

    vor: Bei der zweiten Variante hat man einfach einen Schritt weniger.
    Was er mit den statischen Membern meint, weiß ich allerdings auch nicht.



  • Genau, man spart sich einen Schritt.
    Außerdem kann man Referenzen und "const" member nur über die Initialisierungsliste initialisieren. Und "const" sollte natürlich immer angestrebt werden 🙂

    Was der Umweg über statische Member soll, kann ich mir aber auch nicht erklären.



  • Ich schätze was er meint ist, dass man Membervariablen, die man statisch machen kann auch statisch machen sollte. Diese werden dann natürlich außerhalb der Klasse definiert (initialisiert) und tauchen im Konstruktor gar nicht mehr auf.



  • danke für die Antworten



  • Ich schätze was er meint ist, dass man Membervariablen, die man statisch machen kann auch statisch machen sollte. Diese werden dann natürlich außerhalb der Klasse definiert (initialisiert) und tauchen im Konstruktor gar nicht mehr auf.

    Nee nich ganz ....
    In der init liste kannst natuerlich nur einen Konstruktor deines members erreichen ....
    wenn die klasse deines Members aber nicht den passenden Konstruktor bietet, aber einen Copykonstruktor hasst ... kannst da was mit einer aus der Initialisierungsliste aufrufenden Funktion taetigen. Normale Methoden fallen nateurlich raus, also sind nur statische Methoden ne Wahl, wenn man die funktion mit zur klasse zu haben will (weil sie einfach zugehoert)

    Beispiel:

    du uebergibst nen String als Pfad, der Pfad wird logisch nie im Object geaendert (Kandidat fuer const). Du willst beim parameter bisserl toleranter sein, den aber bei der Uebernahme (Konstruktion) schon mal bisserl bearbeiten (Beispielsweisse backslashes durch slashes ersetzen o.ä. )

    - Du hast die moeglichkeit ... das ausserhalb deiner klasse zu machen, waer aber doof wenn das logisch eher zu den besonderheiten deiner Klasse zaehlt.
    - Du hasst die moeglichkeit das im konstruktor nach der init liste zu machen, dein string wird dann aber ned const
    - statische methode.

    class Test
    {
    public:
        Test(const char * Pfad):
        mPfad(editString(Pfad)) // editstring aufrufen damit das ding const bleibt
        {
        }
        ~Test(){}
    private:    
        const std::string mPfad; 
    
        // und damit das editString funktioniert und es an die klasse gehoert ... 
        static std::string editString(const char * Pfad)
        {
            std::string strreturn(Pfad ? Pfad : std::string()); 
            // iergendwie noch bearbeiten ^^
    
            return strreturn;
    
        }
    };
    

    Sicher dooefes beispiel, da anderen zum thema sicher STL spezifisch performantere dinge einfallen ^^
    Zumindest meint ich sowas mit "notfalls statische member" ... um code aus dem konstruktor auslagern zu koennen.

    Ciao ...



  • Mir ist da noch was aufgefallen: RHBaum schrieb:

    lagere so viel als möglich in die initialisierungslisten aus.

    Nun, was ist aber, wenn ich meine ganz am Anfang geschilderte Situation mit mehreren Konstruktoren habe. Sagen wir mal

    class Klasse
    {
    public:
    
        Klasse ();
        Klasse (std::string name);
    
    private:
    
        std::string name;
        int anzahlLeben;
        int punkte;
    };
    
    Klasse::Klasse ():
        name ("Kein Name"),
        anzahlLeben (3),
        punkte (0)
    {
    }
    

    Beim zweiten Konstruktor hätte ich jetzt nur die Möglichkeit
    a) alle Werte nochmal zu initialisieren, was redundant wäre
    b) alles in eine Init-Funktion auszulagern und diese in beiden Konstruktoren aufzurufen, was bedeuten würde, daß die Initialisierungsliste komplett rausfällt.
    Was wäre hier also zu tun? Für so einen Fall würde ich eben das hier vorschlagen:

    Klasse::Klasse (std::string name):
        Klasse () //Aufrufen des anderen Konstruktors
    {
        this->name=name;
    }
    

    Aber das geht ja leider nur bei Ableitungen.

    P.S.: Ja, ja, ich weiß: Default-Parameter wären auch noch möglich. Aber das hier ist ja nur ein Beispiel. Es könnte ja auch noch was ganz anderes übergeben werden, wofür es keine Default-Parameter gibt.



  • 1. namensgebung !

    gleiche namen fuer member der klasse und parameter macht die sache ned uebersichtlich ^^ Hab 2 mal ueberlegen muessen was er da tut 🙂

    2. default parameter sind genau dafuer da ^^

    3. du kannst in der initliste funktionen aufrufen die mit deren rueckgabewert du den member initialisierst ... nutze dies ....

    4. Du wehrst dich eklatant gegen, die member in den unterschiedlcihen konstruktoren expliziet mehrmals mit dem selben wert zu belegen. Da kommst nich drumherum, die werte anzugeben, der compiler kann ja nich wissen das die werte gleich sein sollen. Wenn du redundanz befuerchtest, lagere auch Default werte aus ...

    class Klasse
    {
    public:
    
        Klasse ();
        Klasse (std::string name);
    
    private:
        static int DefaultAnzahlLeben() { return 3; }
        static int StartPunkte() { return 0; }
        static std::string DefaultName() {return std::string("Kein Name"); }
    
        std::string     mName;
        int             mAnzahlLeben;
        int             mPunkte;
    };
    
    Klasse::Klasse ():
        mName (DefaultName()),
        mAnzahlLeben (DefaultAnzahlLeben()),
        mPunkte (StartPunkte())
    {
    }
    
    Klasse::Klasse (const std::string & name):
    mName (name),
    mAnzahlLeben (DefaultAnzahlLeben()),
    mPunkte (StartPunkte())    
    {
    
    }
    

    C++ soll flexiebel und performant sein !
    C++ beansprucht aber nicht den Titel "Programmiersprache mit dem wenigsten Tippaufwand" fuer sich ... im Gegenteil lieber bisserl mehr tippern dafuer flexibel und performant bleiben als eigentlich fast logische codezeilen nich noch mal schreiben zu muessen ...

    Und fuer viele solcher faelle gibts mittel und wege den tippaufwand zu minimieren ... man muss sie nur als solche akzeptieren ^^

    Default parameter sind eine möglichkeit ....

    Konstruktoren ineinander verschachteln geht nun mal nicht ! verwerf den gedanken. nen konstruktor laeuft durch und ruft keinen anderen konstruktor auf !

    Alternativ kannst du aber Temporaere Objekte, wenn du schon mal geschriebene konstruktoren verwenden willst ... so laufen die konstruktoren auch immer durch. Nur sowas hab ich noch nie wirklich gebraucht ^^ !

    Klasse::Klasse (const std::string & name, const Klasse & rxDefault = Klasse() ):
    mName (name),
    mAnzahlLeben (rxDefault.mAnzahlLeben),
    mPunkte (rxDefault.mPunkte)    
    {
    
    }
    

    Ciao ...


Anmelden zum Antworten