Klasse instanziieren, Konstruktor aber später aufrufen

Hallo,
ich habe ein größeres Projekt am laufen und ich bin jetzt schon an mehreren Stellen auf dieses Problem gestoßen, habe bisher jedoch noch keinen wirklich schönen Workaround gefunden. Ich versuche möglichst objektorientiert zu arbeiten, da ich hierbei Neuling bin und mir Kenntnisse erarbeiten möchte.

Ich habe quasi eine Klasse "Dimmer", welche typische Dimmfunktionen übernimmt. Da eine gewisse Anzahl an LEDs existieren, welche logisch zu einer Einheit zusammengefasst werden können, sollen diese in einer 'übergeordneten' Klasse LEDtop zusammengefasst werden um später bspw. Lichtläufe darzustellen. Jede einzelne LED hat jedoch einen eigenen Pin, der die jeweilige LED ansteuert. Gerne würde ich die Pins der LEDs im Hauptprogramm definieren und dann quasi über den Konstruktor 'durchreichen':

main.cpp

#define LED1 15
#define LED2 18
RemoteDebug Debug;
Energiemgmt Energie(&Debug);

LEDtop ledtop(&Debug, &Energie, LED1, LED2);

void loop() {
  ledtop.LEDtoploop();
}

LEDtop.h

class LEDtop
{  
  public: 
  LEDtop(RemoteDebug *ptrdebug, Energiemgmt *Energiebla, int pin1, int pin2);
  void LEDtoploop();
  
  private:
  RemoteDebug *debugptr;
  Energiemgmt *Energie;
  int pin1;
  int pin2; 
};

LEDtop.cpp

LEDtop::LEDtop(RemoteDebug *ptrdebug, Energiemgmt *Energiebla, int pin1, int pin2){
  this->pin1 = pin1;
  this->pin2 = pin2;
  debugptr = ptrdebug;
  Energie = Energiebla;
  Dimmer Dimm1(pin1,0, debugptr, Energie);
  Dimmer Dimm2(pin2,1, debugptr, Energie);  
}

void LEDtop::LEDtoploop(){
  Dimm1.Dimmerloop();
  Dimm2.Dimmerloop();  
}

Als Fehlermeldung bekomme ich einen:

LEDtop.cpp:25: error: 'Dimm1' was not declared in this scope

   Dimm1.Dimmerloop();

   ^

Was mir irgendwie auch einleuchtet. Die Objekte Dimm1 und Dimm2 werden im Konstruktor erzeugt und existieren anschließend nicht mehr, sobald der Konstruktor durchlaufen wurde. Meine Frage wäre jetzt, ob es eine Möglichkeit gibt diesen Fehler zu umschiffen. Beispielsweise in der LEDtop.h die Objekte Dimm1 und Dimm2 bereits zu erzeugen, die Parameter der Klassen jedoch erst im Konstruktor der Klasse LEDtop zu übergeben. Geht das?

Man könnte natürlich jetzt auch die Dimmer-Klasse ändern in einen leeren Konstruktor und eine Setup-Methode hinzufügen um hierbei die Parameter (wie die Pins) zu übergeben. Dies scheint mir aber irgendwie übertrieben kompliziert?!?

Vielleicht habt ihr ja noch andere Ideen.

Gruß und vielen Dank
Sven

Ich würde das gerne mal von A-Z durchgucken können. Könntest Du mal den/die vollständigen Codeteile posten?

Gruß

Gregor

was ich von deiner Beschreibnung noch nicht ganz verstehe:

was macht deine “quasi eine Dimmer Klasse” genau? Eignet sich diese tatsächlich zum vererben in eine LED Klasse? Oder stellt sie nur einen Dim-Level zur Verfügung den (unter andereim) die LED-Klasse nutzen kann?

Mach bitte einen kompilierbaren Mini-Sketch.

Klasse instanziieren, Konstruktor aber später aufrufen

Unmöglich.
Die Konstruktorkette instanziiert das Objekt.
Das geschieht also immer gleichzeitig.

habe bisher jedoch noch keinen wirklich schönen Workaround gefunden. Ich versuche möglichst objektorientiert zu arbeiten

Das Wort “Workaround” sagt mir, dass du da ein Problem in deiner Gedankenwelt hast.
Instantiieren und Konstruktor ist dasselbe.

Um andere Konstruktoren aufzurufen gibt es Initialisierungslisten. Das geht für Konstruktoren einer Oberklasse, oder einer Klasse die als Variable enthalten ist, oder ein überladener Konstruktor der eigenen Klasse.
Damit werden auch generell Variablen (und Konstanten!) richtig initialisiert anstatt das im Konstrukorkörper selbst zu tun. C++ unterscheidet sich da von anderen Sprachen

Wie gesagt ist dein Aufbau aber eventuell generell keine so gute Idee. Um eine Anzahl an LEDs darzustellen bietet sich vielleicht ein Array aus LED Objekten an. Dann kann man darüber in Schleifen iterieren

Außerdem solltest du Referenzen statt Zeiger verwenden, es sei denn ein Zeiger muss wirklich NULL sein können

noiasca:
was macht deine "quasi eine Dimmer Klasse" genau? Eignet sich diese tatsächlich zum vererben in eine LED Klasse? Oder stellt sie nur einen Dim-Level zur Verfügung den (unter andereim) die LED-Klasse nutzen kann?

Ich denke man hätte das auch anders über Vererbung lösen können, aber aktuell eignet sich die Dimmer-Klasse nicht dazu. Sie übernimmt nur mehrere Funktionen um eine LED zu dimmen und wird dann von der LED-Klasse genutzt.

michael_x:
Das Wort "Workaround" sagt mir, dass du da ein Problem in deiner Gedankenwelt hast.
Instantiieren und Konstruktor ist dasselbe.

Ich glaube bei Weitem nicht nur ein Problem. :smiley: :smiley:
Ich bin so langsam mit meinen Progammierfähigkeiten echt an einem Punkt, wo es nur schlimmer wird. Ich stoße immer wieder auf Probleme, welche ich durch irgendwelche Workarounds umschiffe, die ich selbst nur so semi verstehe. Hauptsache es funktioniert, ich mache überhaupt noch Fortschritte und verliere nicht die Lust. Eigentlich bräuchte ich vermutlich am besten einen Tutor, der sich neben mich setzt und mir zeigt wies richtig geht. Prozedural Programmieren geht so einigermaßen (ausser Referenzen und Zeiger. Da hab ich auch noch nicht so den Durchblick), aber der Umstieg auf Objektorientierung macht mir enorme Schwierigkeiten. Vorallem auch wie man Funktionen am sinnvollsten aufteilt und miteinander verknüpft. Es gibt hierbei tausende Möglichkeiten um ein Problem anzugehen. Irgendwie ist die Aufteilung bei realen Projekten nie so offensichtlich wie bei den häufig genannten Beispielklassen Mensch, Student, Professor o.ä. ::slight_smile:

Ich hänge euch nochmal einen vollständigen, lauffähigen Sketch an, in welchem auch relevante Teile des Projektes beschrieben sind. Ich hab versucht nicht zu wenig aber auch nicht zu viel darzustellen. Ich hab versucht die Gründe für das Design als Kommentar zu erläutern. Vieles ergibt sich erst durch die Zusammenhänge. Falls es zu viel oder zu wenig ist, sagt bitte bescheid. Sorry auch dafür, dass hier jegliche Coding-Conventions fehlen. Nach etlichem Debuggen ist mir mittlerweile auch bewusst geworden, warum das sinnvoll ist. :-[ Das nächste mal dann...

Vielleicht könnt ihr mir ja n bissl Feedback geben, was so grobe Schnitzer sind und warum und wie man das aktuelle Problem durch besseres Design umschifft, um wenigstens ein Problem in meiner Gedankenwelt in Ordnung zu bringen :smiley:

main.cpp

/*
 * Neben den aufgezeigten Funktionen gibt es noch weitere Elemente im Projekt, die verwendet werden.
 * Benutzt wird ein ESP32. Es existiert derzeit ein Webserver um die ganzen Funktionen anzusteuern.
 * Nach und nach sollen jedoch viele der Funktionen automatisiert werden.
 * 
 * Es gibt ausserdem noch Debugging via Telnet und Seriell. Desweiteren kann der µC via OTA geflasht werden.
 * Diese Funktionen sind aufgrund des Umfangs in diesem Sketch nicht dargestellt.
 * 
 * Als weitere Funktionen sind vorgesehen: 
 * -Messen des Stromflusses mehrerer LED-Leisten über Shunts.
 * -Die Ansteuerungen eines WS2812-LEDStreifens, Temperatur und Feuchtemessung, Schalter per kapazitivem Touch. 
 * -Kommunikation, Erweiterungen
 * 
 * Die Hardware ist fertig und muss nur nacheinander in Betrieb genommen werden.  
 */
#include "Energiemgmt.h"
#include "LEDtop.h"

#define PinLEDTop1 19
#define PinLEDTop2 18
#define PinLEDTop3 17
#define PinLEDTop4 16

Energiemgmt Energie;
LEDtop ledtop(&Energie, PinLEDTop1, PinLEDTop2);

void setup() {
  Energie.setupRelais(22);
}
void runonce(){
  //wird über einen Webserver realisiert:
  //über den Webserver können dann verschiedene Funktionen ausgeführt werden.
  ledtop.dimman();
}
void loop() {
ledtop.LEDtoploop();
}

Energiemgmt.cpp:

#include "Energiemgmt.h"

Energiemgmt::Energiemgmt(){
}

void Energiemgmt::setupRelais(int PinLEDtop){
  LEDtop.relaissetup(PinLEDtop);
  
}
void Energiemgmt::toggleLEDtop(){
  if (LEDtop.getstate()==0)
  LEDtop.set(1);
  else 
  LEDtop.set(0);
}
void Energiemgmt::setLEDtop(bool state){
  LEDtop.set(state);
}
int Energiemgmt::getLEDtop(){
  return LEDtop.getstate();
}

Relais::Relais(){
}
void Relais::relaissetup(int newpin){
  Relaispin = newpin;
  digitalWrite(Relaispin, LOW);
  pinMode(Relaispin, OUTPUT);    
}
void Relais::set(bool newstate){
  if (newstate == true){
    digitalWrite(Relaispin, HIGH);
    state = 1;
  }
  else
  {  
    digitalWrite(Relaispin, LOW);
    state = 0;
  }

}
int Relais::getstate(){
return state;  
}

Energiemgmt.h

/*
 * Energiemanagement regelt mehrere Relais, welche unterschiedliche Netzteile schalten.
 * Beispielhaft ist hier nur ein Relais aufgeführt.
 * 
 * Hintergrund für die separate Energiemgmt-Klasse neben Relais ist, dass mehrere Relais gebündelt geschaltet werden können 
 * um beispielsweise eine Not-Aus-Funktion realisieren zu können.
 * 
 * Ein Energiemgmt-Objekt kann hinterher von anderen Klassen verwendet werden 
 * um den jeweiligen Trafo für die jeweilige Spannungslage einzuschalten, welche benötigt wird.
 * Wenn die jeweilige Spannungslage nicht mehr benötigt wird, soll der jeweilige Trafo über das Relais ausgeschaltet werden.
 */

#ifndef __Energiemgmt__
#define __Energiemgmt__

#include <Arduino.h>

class Relais
{  
  public: 
    Relais();
    void set(bool newstate);
    void relaissetup(int newpin);
    int getstate();
  
  private:
    int Relaispin;
    int state;
};

class Energiemgmt
{
    public:
      //Konstruktor:
      Energiemgmt();
      
      void setupRelais(int PinLEDtop);
      void setLEDtop(bool state);
      void toggleLEDtop();
      int getLEDtop();    
   private:
      Relais LEDtop;
};
#endif

LEDtop.cpp

#include "Dimmer.h"
#include "Energiemgmt.h"
#include "LEDtop.h"

LEDtop::LEDtop(Energiemgmt *Energiebla, int pin1, int pin2){
  this->pin1 = pin1;
  this->pin2 = pin2;
  Energie = Energiebla;
  Dimmer Dimm1(pin1,0);       
  Dimmer Dimm2(pin2,1);  
}

void LEDtop::LEDtoploop(){
  //Dimm1.Dimmerloop();                       //Hier ist der Fehler. Dies funktioniert logischerweise nicht.
  //Dimm2.Dimmerloop();                       //Hier ist der Fehler. 
}
void LEDtop::dimman(){
  Energie->setLEDtop(true);
  delay(800); //wird später noch durch eine Millis()-funktion ersetzt
  //Das Warten ist notwendig, da der Trafo für die LEDs erst 800ms nach Bestromung die LEDs bestromt.
  //Dimm1.dimmon();                          //Hier ist der Fehler. 
  //Dimm2.dimmon();                          //Hier ist der Fehler. 
}

LEDtop.h

/*
 * LEDtop soll 4 LEDStreifen logisch bündeln. (hier nur 2 dargestellt, diese Klasse ist gerade in Bearbeitung)
 * Viele der nachfolgend beschriebenen Funktionen existieren noch nicht und sind zum Verständnis beschrieben:
 * 
 * Hier soll später realisiert werden, dass die 4 LEDStreifen nacheinander mit zeitlichem versatz an-gedimmt werden können um einen Lauf-Effekt zu erzeugen.
 * 
 * Da die 4 LEDStreifen an einem gemeinsamen Netzteil hängen, soll an dieser Stelle auch das Energiemgmt automatisiert werden.
 * D.h. Wenn die LEDstreifen angeschaltet werden sollen, soll durch das Energiemgmt zuerst der zugehörige Trafo bestromt werden und nach einem delay von 800ms die LEDs andimmen.
 * Wenn alle LEDStreifen aus sind, soll nach einer gewissen Zeit (vlt 30s) auch der zugehörige Trafo stromlos geschaltet werden.
 */
#ifndef __LEDtop__
#define __LEDtop__

#include "Energiemgmt.h"

class LEDtop
{  
  public: 
  LEDtop(Energiemgmt *Energiebla, int pin1, int pin2);
   
  void LEDtoploop();
  void dimman();
  
  private:
  Energiemgmt *Energie;
  int pin1;
  int pin2;
};
#endif

Dimmer.cpp

#include <Arduino.h>
#include "Dimmer.h"
#include "Energiemgmt.h"
#include <esp32-hal-ledc.h>
#include <math.h>

Dimmer::Dimmer(int dimmpin, int dimmchannel){
  this->dimmpin = dimmpin;
  this->dimmchannel = dimmchannel;
  currdimmstate = 0;
  freq = 5000;
  resolution = 10;
  dutymax = pow(2,resolution);
  ledcSetup(dimmchannel, freq, resolution);
  ledcAttachPin(dimmpin, dimmchannel);
}

void Dimmer::dutytonorm(){
  float erg;
  erg = 100.0*((float)dutycycle/(float)dutymax);
  currdimmstate = (int)erg;
}
int Dimmer::normtoduty(){
  float erg;
  erg = ((float)currdimmstate /100.0)*(float)dutymax;
  return (int)erg;
}
int Dimmer::sollnormtoduty(){
  float erg;
  erg = ((float)solldimmstate/100.0)*(float)dutymax;
  return (int)erg;
}

void Dimmer::calcnextduty(){
  if (dimmup == 1)
  {
    if (dutycycle <= dutymax)
    {
      dutycycle++;
    }
  }
  else
  {
    if(dutycycle > 0)
    {
      dutycycle--;
    }
  }
}

void Dimmer::setoff(){
  ledcWrite(dimmchannel, 0);
  currdimmstate = 0;
  dutytonorm();
}
void Dimmer::seton(){
  ledcWrite(dimmchannel,1024);
  currdimmstate = 100;
  dutytonorm();
}
void Dimmer::checkupdown(){
  if (solldimmstate>currdimmstate)
  {
    dimmup = 1;
    active = 1;
  }
  else if (solldimmstate <currdimmstate)
  {
    dimmup = 0;
    active = 1;
  }
  else 
  {
    active = 0;
  }
}
void Dimmer::setperiod(int periode){
  period = periode;
}

void Dimmer::dimmto(int to){
  solldimmstate = to;
  setperiod(3);
  checkupdown();
}
void Dimmer::dimmon(){
  setperiod(3);
  solldimmstate = 100;
  checkupdown();
}
void Dimmer::dimmoff(){
  setperiod(3);
  solldimmstate = 0;
  checkupdown();
}

void Dimmer::Dimmerloop(){
  currentMillis = millis();
  //erledigt das eigentliche faden:
  if(active == 1)
  {
    if (currentMillis - startMillis >= period)
    {
      calcnextduty();
      ledcWrite(dimmchannel, dutycycle);
      dutytonorm();
      
      startMillis = currentMillis;
    }
    if (dutycycle == sollnormtoduty())
    active = 0;
  }
}

dimmer.h

/*
 * Dimmer soll einzelne LED-Streifen über ein MOSFET per PWM dimmen können.
 * Funktionen sind hier Anschalten, Ausschalten, An-Dimmen, Aus-Dimmen mit unterschiedlicher Dauer
 * Ausserdem ist es möglich auf eine bestimmte Helligkeit zu dimmen.
 * Die Dimmstufe soll auf einer normierten Skala von 0-100 angegeben werden können.
 * 
 * Später soll die Klasse eventuell erweitert werden um den Dimmer zu linearisieren, sodass ein gleichmäßigerer Dimmeffekt zustande kommt
 */
#ifndef __Dimmer__
#define __Dimmer__

#include "Energiemgmt.h"

class Dimmer
{  
  public: 
  Dimmer(int dimmpin, int dimmchannel);
   
  void Dimmerloop();
  void setoff();
  void seton();
  void dimmon();
  void dimmoff();
  void dimmto(int to);
  int normtoduty();
  void setperiod(int periode);
  void dutytonorm();
  
  private:
  int freq;
  int resolution;
  int currentMillis;
  int startMillis;
  int dutycycle; //zeigt den nächsten dimmwert je nach Auflösung an (bsp 0...1023) 
  int dimmchannel; 
  int dimmpin; 
  int currdimmstate; //wie der aktuelle dimmstatus der LED ist ( von 0 bis 100)
  int solldimmstate; //anforderung wie der nächste Dimmstatus sein soll;
  int sollnormtoduty();
  int dutymax;
  int period;  
  int active; // zeigt ob der dimmer aktiv sein soll oder fertig ist
  void calcnextduty(); // berechnet den nächsten zu dimmenden Duty-Wert
  int dimmup; //wenn dimmup = 1 hochdimmen, wenn dimmup = 0 runterdimmen
  void checkupdown();
};
#endif

Der Hauptpunkt ist erst mal dass du dir das ansiehst und verinnerlichst:

C++ Primer
https://en.cppreference.com/w/cpp/language/initializer_list
https://www.learncpp.com/cpp-tutorial/8-5a-constructor-member-initializer-lists/

Löse dich davon wie Konstruktoren in Sprachen wie Java funktionieren. In C++ werden Dinge bei Erstellen des Objekts initialisiert. Also praktisch außerhalb oder vor dem Konstruktor. Wenn du im Konstruktor bist, ist es schon zu spät. Dort kannst du Dinge nur noch zuweisen Bestenfalls überschreibst du dann die ursprüngliche Initialisierung. Und Dinge wie Konstanten oder Referenzen kann man dann nicht mehr initialisieren

Und zu Referenzen:
https://de.wikibooks.org/wiki/C%2B%2B-Programmierung/_Weitere_Grundelemente/_Referenzen

Außerdem solltest du mehr Gedanken zu Datentypen machen anstatt immer nur int zu verwenden. Die millis()-Zeit z.B. muss unsigned long sein

Vielen Dank an Serenifly. Das ist genau das, was mir gefehlt hat.

Die Initialisierungsliste scheint ja irgendwie die eierlegende Wollmilchsau des Konstruktors zu sein. Gibt es Fälle, bei denen man den Konstruktorrumpf der Initialisierungsliste vorzieht? Die Initialisierungsliste wird ja eh immer direkt vor dem Konstruktorrumpf ausgeführt und kann auch deutlich mehr. Gibt es trotzdem Fälle wo man den Konstruktorrumpf verwenden muss? Warum gibt es den Konstruktorrumpf überhaupt?

Um meine Lösung des Problems vom Eingangsbeitrag für die Nachwelt festzuhalten:

LEDtop.h:

class LEDtop
{  
  public: 
  LEDtop(RemoteDebug *ptrdebug, Energiemgmt *Energiebla, int pin1, int pin2);
  void LEDtoploop();
  
  private:
  RemoteDebug *debugptr;
  Energiemgmt *Energie;
  Dimmer Dimm1;
  Dimmer Dimm2;
};

LEDtop.cpp:

LEDtop::LEDtop(RemoteDebug *ptrdebug, Energiemgmt *Energiebla, int pin1, int pin2):debugptr(ptrdebug), Energie(Energiebla), Dimm1(pin1,0,debugptr, Energie), Dimm2(pin2,1,debugptr,Energie) {}

void LEDtop::LEDtoploop(){
  Dimm1.Dimmerloop();
  Dimm2.Dimmerloop();  
}

Danke auch nochmal für den Hinweis zu den Datentypen. Das mit den millis() hab ich direkt geändert. Da wär ich vermutlich irgendwann auf unerklärliches Verhalten gestoßen, sobald der int überläuft. :cold_sweat: Auch hab ich in meinem orginal Sketch mal noch einige andere Datentypen überarbeitet. Was nur 1 oder 0 sein kann, als bool und I/O-pins nur als byte. Bisher hatte ich das Problem noch nicht, dass mir der Speicher ausgegangen ist, aber vielleicht sollte ich mir das echt direkt angewöhnen, mehr darüber nachzudenken ::slight_smile:

Viele Grüße und danke nochmal!

Svenjamin:
Warum gibt es den Konstruktorrumpf überhaupt?

Weil es Klassen geben kann/wird, die nach der Initialisierung von Werten, in Abhängigkeit davon noch was zu erledigen haben.

Gruß Tommy

Svenjamin:
... Hauptsache es funktioniert, ich mache überhaupt noch Fortschritte und verliere nicht die Lust. ...

Wenn Du mich fragst, hast Du die Lust bereits verloren.

Wenn die Lernerei nicht unter Zeitdruck stattfindet: Mach' eine Pause. Fahre erst dann mit dem Lernen fort, wenn Du ein bisschen über Objektorientierung nachdenken konntest.

Die Idee der OOP ist genial.

Als ich mit C++ anfing, habe ich mehrere Anläufe gebraucht, bis ich mein Buch dazu wirklich durchgelesen hatte. Mein Buch stammt von 2003, so richtig gelesen und kapiert hatte ich es ungefähr '08. Vorteilhaft für mich war halt, dass ich ziemlich lang keine prozedurale Programmierung mehr praktiziert hatte und mein Buch direkt in C**++** einsteigt. Das Buch gibt es auch online, der Einstieg in das Prinzip der OOP ist IMO sehr gut. Guck da: Grundlagen der objektorientierten Programmierung in C++

Gruß

Gregor

Gibt es trotzdem Fälle wo man den Konstruktorrumpf verwenden muss? Warum gibt es den Konstruktorrumpf überhaupt?

Wenn man Funktionen oder generell komplexeren Code ausführen muss. z.B. einen C String mit strcpy() kopieren. Oder Werte auf Plausibilität prüfen

Warum gibt es den Konstruktorrumpf überhaupt?

Eher ist die Initialisierungsliste der Sonderfall, mit dem es möglich ist, auch Konstanten einen Wert zuzuweisen. Den Minimal-Rumpf { } braucht der Compiler in jedem Fall, um überhaupt eine Funktionsdefinition zu erkennen.

michael_x:
Eher ist die Initialisierungsliste der Sonderfall

Nein. Die sollte der Standard sein, auch wenn man es nicht unbedingt bräuchte. Sonst werden Werte erst Default initialisiert und dann überschrieben

Serenifly:
Nein. ...

Doch :slight_smile:

Ich habe mir angewöhnt, auch für Funktionen, die nur eine Initialisierungsliste benötigen, eine „richtige“ Funktion mit allem Pipapo in die Definitionsdatei zu schreiben. Wenn ich nur die ausdrucke, habe ich wirklich alle Funktionen im Ausdruck und kann darin herumkritzeln, wenn ich z. B. den Code umräume. Ansonsten wundere ich mich ab und zu, warum denn „die eine Funktion“ nicht im Ausdruck auftaucht.

Es ist wohl auch eine Frage des persönlichen Stils.

Gruß

Gregor

Ich sehe einen Konstruktor als Sonderfall einr Methode:

  • braucht einen Rumpf {}
  • kann mit verschiedenen Parametern existieren
  • hat keinen Rückgabe-Datentyp (auch nicht void)
  • wird bei der Instantiierung ausgeführt.
  • kann vor dem Rumpf eine Initialisierungsliste haben.

Ob man bevorzugt diese Initialisierungsliste verwendet, oder ob der Compiler das auch anders genauso optimal hinkriegt, ist nicht ganz so wichtig. Als Lerneffekt, damit man sich dran gewöhnt, empfiehlt sich sicher die Schreibweise per Liste.

Man könnte auch so fragen:

Was ist älter, der Methodenrumpf, oder die Initialisierungsliste?