Initialisierungen hinter dem Konstruktor - was soll das hier?



  • Schaut euch mal bitte dieses Beispiel (aus der Irrlich-3D-Engine) an:

    class ISceneNode : public IUnknown 
       { 
       public: 
    
          //! Constructor 
          ISceneNode(   ISceneNode* parent, ISceneManager* mgr, s32 id=-1, 
                   const core::vector3df& position = core::vector3df(0,0,0), 
                   const core::vector3df& rotation = core::vector3df(0,0,0), 
                   const core::vector3df& scale = core::vector3df(1.0f, 1.0f, 1.0f)) 
             : IsVisible(true), ID(id), Parent(parent), SceneManager(mgr), 
                AutomaticCullingEnabled(true), DebugDataVisible(false), 
                TriangleSelector(0), RelativeTranslation(position), 
                RelativeRotation(rotation), RelativeScale(scale) 
    
          { 
             if (Parent) 
                Parent->addChild(this); 
    
             updateAbsolutePosition();          
          }
    
          // ... hier folgt noch diverses aus dieser Klasse (s.u.)
    

    Anzumerken wäre noch, dass die Klasse IUnknown weder Vorgängerklasse noch Membervariablen besitzt. Außerdem sind IsVisible, ID, ... und der ganze Rest hinter dem ':' des Konstruktors Membervariablen der Klasse ISceneNode.

    Ich kenne diese Art der Initialisierung bisher eigendlich nur bei der Konstruktion von Vorgängerklassen (auch bei Mehrfachvererbung), wenn es zu Anzahl und Typ der Parameter zur Konstruktion der abgeleiteten Klasse (hier ISceneNode) keinen äquivalenten Konstruktor in den/der Vorgängerklasse(n) gibt.

    Fragen
    1. Welchen Sinn macht es im obigen Beispiel die ganzen Membervariablen von ISceneNode hinter dem Doppelpunkt zu initialisieren, was immerhin ziemlich unübersichtlich ist (zumal das alles in wenige Zeilen gepackt wurde), anstelle es am Anfang des Konstruktor-Bodies zu machen.

    2. Jemand hat mir erzählt, dieses Verfahren (oben) wäre schneller. Kann das hier wer bestätigen?

    3. Was mich auch wundert: Wa ist das für eine Syntax: Membervariable(Wert) ?
    Bei ID etwa ist der Typ 'signed int', nichtmal ne Klasse ...

    Grüße
    Ayman



  • Initialisierungsliste nennt sich das. Für alle Objekte, die einen argumentlosen Constructor (oder entsprechende default-Argumente) besitzen, brauchst du das nicht. Für diese ist es im Prinzip egal, ob du's im Body oder in der Liste initialisierst. Sonst ist es dafür gedacht, Constructors aufzurufen, wie du schon bemerkt hast. Die Reihenfolge ist zu beachten: die Initialisierungsliste wird in der Reihenfolge abgearbeitet, in der die member bei der Klassendefinition angegeben sind, nicht in der Reihenfolge der Liste. Die meisten Compiler warnen hier aber ohnehin.

    1. Geschmackssache
    2. Unsinn
    3. Funktioniert für alle Typen



  • es is weder schneller noch langsamer, aber du brauchst diese art der initalisiereung flass du const atrributte hast. Ich meine du kannst const attribute nur hier initialisieren. Sonst oft verwendet bei der Vererbung. Der rest ist eine Frage des codestyles

    mfg



  • spjoe schrieb:

    aber du brauchst diese art der initalisiereung flass du const atrributte hast.

    Referenzen fallen auch darunter...



  • Naja, aber eigentlich dürfte Initialisierung der Art:

    class test
    {
    private:
        big_and_slwo_to_create_or_change bastcorj;
    public:
        test(unsigned bastcorj_arg):bastcorj(bastcorj_arg){};
    }
    

    Schneller sein als

    class test
    {
    private:
        big_and_slwo_to_create_or_change bastcorj;
    public:
        test(unsigned bastcorj_arg){bastcorj=bastcorj_arg;};
    }
    

    Denn beim ersten wird bastcorj mit dem Konstruktor initialisiert, beim 2. mal zwar auch, aber später wieder verändert - redundant...



  • es kann durchaus schneller sein.



  • spjoe schrieb:

    es is weder schneller noch langsamer, aber du brauchst diese art der initalisiereung flass du const atrributte hast. Ich meine du kannst const attribute nur hier initialisieren. Sonst oft verwendet bei der Vererbung. Der rest ist eine Frage des codestyles

    mfg

    Das stimmt so nicht. Wenn du Variablen im Konstruktor statt in der Initialisierungsliste einen Wert zuweist, kann das u. U. ineffizienter sein, da zwei Memberfunktionen aufgerufen werden: Einmal der Defaultkonstruktor und einmal der Zuweisungsoperator.

    Meyers: Effektiv C++, Lektion 12: Bevorzugen Sie Initialisierung gegenüber Zuweisung im Konstruktor



  • Das ist durchaus richtig, aber das sollte eh jedem klar sein, zumal hier komplett verschiedene Funktionen aufgerufen werden.



  • Ringding schrieb:

    Das ist durchaus richtig, aber das sollte eh jedem klar sein, zumal hier komplett verschiedene Funktionen aufgerufen werden.

    Aufgrund der Aussagen scheint das ja nicht so klar gewesen zu sein....



  • CarstenJ schrieb:

    spjoe schrieb:

    es is weder schneller noch langsamer, aber du brauchst diese art der initalisiereung flass du const atrributte hast. Ich meine du kannst const attribute nur hier initialisieren. Sonst oft verwendet bei der Vererbung. Der rest ist eine Frage des codestyles

    mfg

    Das stimmt so nicht. Wenn du Variablen im Konstruktor statt in der Initialisierungsliste einen Wert zuweist, kann das u. U. ineffizienter sein, da zwei Memberfunktionen aufgerufen werden: Einmal der Defaultkonstruktor und einmal der Zuweisungsoperator.

    Meyers: Effektiv C++, Lektion 12: Bevorzugen Sie Initialisierung gegenüber Zuweisung im Konstruktor

    Es ist aber auch nicht verboten den Defaultkonstruktor im Konstruktor zu verwenden
    muss ja nicht

    k{
       var = 12312;
    //kann auch
       var(23432);
    }
    

    verwenden

    mfg



  • das mit dem schneller gilt auf jeden fall schonmal nicht für built-in typen.
    und bei trivialen klassen auch nicht, wenn sie inline-funktions-erweiterung möglich ist. Allerdings sollte man, falls nen Konstruktor vorhanden ist, den in der Initialisierungslist auch nutzen.

    MfG
    DDR-RAM



  • spjoe schrieb:

    CarstenJ schrieb:

    spjoe schrieb:

    es is weder schneller noch langsamer, aber du brauchst diese art der initalisiereung flass du const atrributte hast. Ich meine du kannst const attribute nur hier initialisieren. Sonst oft verwendet bei der Vererbung. Der rest ist eine Frage des codestyles

    mfg

    Das stimmt so nicht. Wenn du Variablen im Konstruktor statt in der Initialisierungsliste einen Wert zuweist, kann das u. U. ineffizienter sein, da zwei Memberfunktionen aufgerufen werden: Einmal der Defaultkonstruktor und einmal der Zuweisungsoperator.

    Meyers: Effektiv C++, Lektion 12: Bevorzugen Sie Initialisierung gegenüber Zuweisung im Konstruktor

    Es ist aber auch nicht verboten den Defaultkonstruktor im Konstruktor zu verwenden
    muss ja nicht

    k{
       var = 12312;
    //kann auch
       var(23432);
    }
    

    verwenden

    mfg

    Verboten ist wenig in C++, aber ich verstehe jetzt den Zusammenhang nicht?!



  • Er meinte, wenn man

    typ a = 123;
    

    schreibt, darf der Compiler das zu

    typ a(123);
    

    umbauen. Das geht aber in dem Beispiel mit der Initialisierungsliste nicht.



  • explicit?



  • spjoe schrieb:

    Es ist aber auch nicht verboten den Defaultkonstruktor im Konstruktor zu verwenden
    muss ja nicht

    k{
       var = 12312;
    //kann auch
       var(23432);
    }
    

    verwenden

    var ist zu dem zeitpunkt ja schon längst konstruiert. nochmal den konstruktor aufrufen ist da nicht so einfach.
    etwas ausformuliert kommt dein beispiel bei mir so an:

    struct foo
    {
       string x;
       foo () 
       {
          x("bar"); //ruft string::string(const char*) auf  [1]
       }
    };
    

    ich hoffe, dir ist klar, dass das nicht stimmt.
    zu zeitpunkt [1] ist x schon default-konstruiert worden. hier würde nurnoch eine zuweisung funktionieren. und diese zuweisung wäre auch wirklich eine zuweisung und keine initialisierung.

    Ringding schrieb:

    Er meinte, wenn man

    typ a = 123;
    

    schreibt, darf der Compiler das zu

    typ a(123);
    

    umbauen. Das geht aber in dem Beispiel mit der Initialisierungsliste nicht.

    wenn er das wirklich meinte, hat das nichts mehr mit membervariablen zu tun gehabt. sehr unsinnig das ganze.
    btw. der compiler darf das auch nur dann "umbauen", wenn der copy-ctor von typ public ist. in wirklichkeit ist typ a = 123; ja eine copy-initialization.

    finix schrieb:

    explicit?

    kann dieses "umbauen" geschickt eingesetzt verhindern. das hat mit dem ursprünglichen thema (elementinitialisierungsliste) aber kaum mehr was zu tun.



  • ayman schrieb:

    Fragen
    1. Welchen Sinn macht es im obigen Beispiel die ganzen Membervariablen von ISceneNode hinter dem Doppelpunkt zu initialisieren, was immerhin ziemlich unübersichtlich ist (zumal das alles in wenige Zeilen gepackt wurde), anstelle es am Anfang des Konstruktor-Bodies zu machen.

    Ich hoffe, dir ist mittlerweile klargeworden, dass die Initialisierungsliste ihren Sinn hat. Zumal du nur mit dieser zB Basisklassen initialisieren kannst. Wenn es dir zu unübersichtlich ist, dann hier mal das Beispiel, wie ich sowas mache

    ISceneNode(ISceneNode* parent, ISceneManager* mgr, s32 id=-1,
        const core::vector3df& position = core::vector3df(0,0,0),
        const core::vector3df& rotation = core::vector3df(0,0,0),
        const core::vector3df& scale = core::vector3df(1.0f, 1.0f, 1.0f))
        : IsVisible(true)
        , ID(id)
        , Parent(parent)
        , SceneManager(mgr)
        , AutomaticCullingEnabled(true)
        , DebugDataVisible(false)
        , TriangleSelector(0)
        , RelativeTranslation(position)
        , RelativeRotation(rotation)
        , RelativeScale(scale)
    {
        // bla
    }
    

    So wird das schon um einiges übersichtlicher.



  • Ich wollte mich nur bei Euch für die umfassende Diskussion bedanken. Jetzt ist mir der Fall klar 🙂

    Grüße
    Ayman


Anmelden zum Antworten