Spezieller Konstruktor -> vielleicht weiß jemand was Genaueres ?



  • Hallo Leute,

    in der Abhandlung “Practical Statecharts in C/C++−Quantum Programming for Embedded Systems“ ist auf Seite 51 folgendes Codefragment zu finden, das paradoxerweise 1:1 auch hier auftaucht https://github.com/PX4/Firmware/blob/master/src/modules/systemlib/state_table.h):

    // generic "event processor" ...
    class StateTable {
    
    public:
    	typedef void (StateTable::*Action)(); //pointer-to-member function
    
    	struct Transition { // inner struct transition (aggregate)
    	Action action;
    	unsigned nextState;
    	};
    
    StateTable(Transition const *table, unsigned nStates, unsigned nSignals) : myTable(table), myNsignals(nSignals), myNstates(nStates) {}
    
    // further content
    
    };
    

    Der Konstruktor macht hier irgendeine Zuordnung – aber wie genau ?

    StateTable(Transition const *table, unsigned nStates, unsigned nSignals) : myTable(table), myNsignals(nSignals), myNstates(nStates) {}
    

    Vielleicht kann mir jemand einen Hinweis geben, wo ich ggf. mal nachlesen könnte – vielleicht ist es aber auch mit wenigen Worten erklärt…

    Vielen Dank fürs lesen





  • Der Konstruktor ist nicht speziell sondern der Regelfall, wenn Parameter übergeben werden.



  • Hallo,
    und danke für das Stichwort - wenn man sich den privaten Teil der Klasse anschaut, macht das Ganze dann vielleicht auch Sinn. Das wäre doch jetzt äquivalent - oder ?

    class StateTable {
    
    public:
        typedef void (StateTable::*Action)(); //pointer-to-member function
    
        struct Transition { // inner struct transition (aggregate)
        Action action;
        unsigned nextState;
        };
    
    //StateTable(Transition const *table, unsigned nStates, unsigned nSignals) : //myTable(table), myNsignals(nSignals), myNstates(nStates) {}
    
    StateTable(Transition const *table, unsigned nStates, unsigned nSignals ) {
    
    	myTable = table;
    	myNsignals = nSignals;
    	myNstates = nStates;
    	}
    
    // further content
    
    private:
    
    	Transition const *myTable;
    	unsigned myNsignals;
    	unsigned myNstates;
    
    }
    


  • Ja. Aber wenns es keinen Grund gibt, nicht die Initialisierungsliste zu verwenden, sollte man das auch nicht tun (sie nicht verwenden, meine ich).
    Die Reihenfolge der Deklarationen der Variablen stimmt nicht mit der Initialisierung im Konstruktor überein. Der Compiler sollte hier eine Warnung ausgeben. In diesem Fall ist das egal, es lässt sich aber leicht vermeiden, also warum nicht?



  • 7x7-7 schrieb:

    Das wäre doch jetzt äquivalent - oder ?

    Nein. Hier werden zunächst alle Member per Defaultkonstriktor angelegt. Anschließend wird ein Wert zugewiesen. Mit der Initialisierungsliste werden die Member mit dem jeweils passenden Konstruktor direkt mit den Werten gefüllt.



  • . Mit der Initialisierungsliste werden die Member mit dem jeweils passenden Konstruktor direkt mit den Werten gefüllt.

    Muss nicht erst ein Member angelegt werden, damit es mit einem Inhalt gefüllen werden kann ? Also auch wenn alles konzertiert ablaufen soll, ist doch die Reihenfolge: Anlegen und dann Zuweisen. Möglicherweise ist es ja eine Performancefrage und möglicherweise ist die Initialisierungsliste dann ja schneller als der Defaultkonstruktor mit diskreten Zuweisungen, was bei Statemachines von Bedeutung sein könnte.



  • 7x7-7 schrieb:

    Muss nicht erst ein Member angelegt werden, damit es mit einem Inhalt gefüllen werden kann ?

    definiere angelegt

    7x7-7 schrieb:

    möglicherweise ist die Initialisierungsliste dann ja schneller als der Defaultkonstruktor mit diskreten Zuweisungen

    Ja. Und möglicherweise geht es gar nicht anders (nämlich wenn es keinen Defaultkonstruktor gibt).



  • In C++ gibt es einen Unterschied zwischen Initialisierung und Zuweisung.

    int a = 42; // Initialisierung
    a = 13; // Zuweisung
    

    Wenn du also im Konstruktorbody die Variablen "initialisierst" machst du also tatsächlich eine Zuweisung.
    Es ist bei dir, wie ich schrieb, äquivalent. Im Endeffekt macht es keinen Unterschied (in diesem Fall).

    Ich weiß ja nicht, wie viele StateTables du erzeugen willst, aber hier würde ich mir keinen Gedanken über Performancegewinn machen. Der Compiler hat ja sowieso noch die Möglichkeit, zu optimieren. Möglicherweise kann er besser optimieren, wenn du die Initialisierungsliste benutzt, da kenne ich mich nicht gut aus.
    Aber nochmal: verwende die Initialisierungsliste einfach immer, wenns geht. Das drückt einfach besser aus, was du erreichen möchtest. Wenn du Objekte hast, die keinen Copy-Konstruktor oder operator= haben, musst du sie ohnehin direkt initialisieren.
    Desweiteren gilt dies auch bei Konstanten. Hier kannst du auch nichts zuweisen.

    class foo
    {
        public:
            foo(int i) : ci(i) { } // ja
            foo(int i) { ci = i; } // nein
        private:
            const int ci;
    };
    

    EDIT: achja, Referenzen müssen natürlich auch initialisiert werden.



  • Hallo,
    und danke für die Diskussion. Ich glaube Du hast nun den entscheidenden Hinweis gegeben:

    Wenn du Objekte hast, die keinen Copy-Konstruktor oder operator= haben, musst du sie ohnehin direkt initialisieren.
    

Log in to reply