Auf Garantie für Intialisierungsreihenfolge vertrauen?



  • @Leon0402 sagte in Auf Garantie für Intialisierungsreihenfolge vertrauen?:

    @john-0 Mal abgesehen davon, dass es im Code steht,

    Wo steht das im Code? Da steht nur eine Aufruf mit einem bestimmten Namen, wenn man jetzt nicht die Library kennt, die Du an dieser Stelle verwendest, kann man nicht wissen, dass das nicht selbst geschrieben ist.

    Das Meyers Singleton wäre bei selbstgeschriebenem Code aber die Lösung das Initialisierungsproblem aus der Welt zu schaffen.



  • @hustbaer Und wenn es mehrere Klassen werden, die den Logger benötigen? Würdest du da wirklich überall den Logger rumreichen?

    Wobei das auch für Unit Tests vermutlich wesentlich einfacher wäre, es so explizit zu machen. Bei der registry wüsste ich jetzt auf Anhieb nicht wie man das mockt. (Mit Unit Tests beschäftige ich mich grade erstmals)



  • @Leon0402 sagte in Auf Garantie für Intialisierungsreihenfolge vertrauen?:

    @hustbaer Und wenn es mehrere Klassen werden, die den Logger benötigen? Würdest du da wirklich überall den Logger rumreichen?

    Logger ist da natürlich schon ein Spezialfall. Ich würde aber auf jeden Fall sagen: entweder muss das alles so gemacht sein dass nichts schlimmes passiert wenn der Logger nicht verfügbar ist (ausser dass evtl. die Log-Messages halt nicht geschrieben werden), oder der Logger sollte explizit übergeben werden.

    Logger über globale/statische Variablen verfügbar machen ist aber mMn. nicht grundsätzlich phöse. Es hat mMn. sogar einen nicht zu verachtenden Vorteil: der Logger kann überall "besorgt" werden ohne dass man gleich viel Code refactoren muss. D.h. man kann auch aus kleinen Utility-Funktionen oder -Klassen "einfach so" loggen. Der Code an dem ich aktuell arbeite hat keine globale Logger Registry, und das ist manchmal schon etwas lästig.

    Was Unit-Tests angeht kann einfach verbieten dass mehrere Unit-Tests im selben Prozess parallel laufen. Dann kann man den Logger immer noch "mocken" indem man halt der globalen Stelle (Logger-Registry oder was auch immer) den Mock unterjubelt bevor man den Test laufen lässt - und nachdem der Test fertig ist wieder entfernt. Parallele Ausführung von Unit-Tests muss dann halt mehrere Prozesse verwenden. Mit CTest geht das z.B. sehr einfach.



  • @hustbaer sagte in Auf Garantie für Intialisierungsreihenfolge vertrauen?:

    Logger ist da natürlich schon ein Spezialfall. Ich würde aber auf jeden Fall sagen: entweder muss das alles so gemacht sein dass nichts schlimmes passiert wenn der Logger nicht verfügbar ist (ausser dass evtl. die Log-Messages halt nicht geschrieben werden), oder der Logger sollte explizit übergeben werden.

    Das ist aber ja irgendwie noch schlimmer ... er würde dann nicht loggen und ich würde es nicht mal bemerken? Das der Logger nicht verfügbar ist, sollte ja i.d.R. nicht vorkommen

    @hustbaer sagte in Auf Garantie für Intialisierungsreihenfolge vertrauen?:

    Logger über globale/statische Variablen verfügbar machen ist aber mMn. nicht grundsätzlich phöse. Es hat mMn. sogar einen nicht zu verachtenden Vorteil: der Logger kann überall "besorgt" werden ohne dass man gleich viel Code refactoren muss. D.h. man kann auch aus kleinen Utility-Funktionen oder -Klassen "einfach so" loggen. Der Code an dem ich aktuell arbeite hat keine globale Logger Registry, und das ist manchmal schon etwas lästig.

    Ja, das sehe ich auch so. Das übergeben scheint mir mühselig hat man ja bei vergleichbarem auch nicht z.B. std::cout ... auch global.

    @hustbaer sagte in Auf Garantie für Intialisierungsreihenfolge vertrauen?:

    Dann kann man den Logger immer noch "mocken" indem man halt der globalen Stelle (Logger-Registry oder was auch immer) den Mock unterjubelt bevor man den Test laufen lässt

    Ah okay, ich wusste nicht inwiefern das möglich ist, das ist gut zu wissen.



  • Man könnte auch einen speziellen Log Appender mocken, der die Nachrichten annimmt, damit man sie im Test dann auswerten kann. z.B. könnte der Test erwarten, dass Log-Nachrichten kommen sollten oder eben nicht.
    Ob das mit spdlog geht, weiß ich nicht.



  • @Leon0402 sagte in Auf Garantie für Intialisierungsreihenfolge vertrauen?:

    @hustbaer sagte in Auf Garantie für Intialisierungsreihenfolge vertrauen?:

    Logger ist da natürlich schon ein Spezialfall. Ich würde aber auf jeden Fall sagen: entweder muss das alles so gemacht sein dass nichts schlimmes passiert wenn der Logger nicht verfügbar ist (ausser dass evtl. die Log-Messages halt nicht geschrieben werden), oder der Logger sollte explizit übergeben werden.

    Das ist aber ja irgendwie noch schlimmer ... er würde dann nicht loggen und ich würde es nicht mal bemerken? Das der Logger nicht verfügbar ist, sollte ja i.d.R. nicht vorkommen

    Naja...

    Wenn du in bestimmten Komponenten/Funktionen nur Fehler loggst (üblich), dann wirst du beim Testen nicht unbedingt merken dass der Logger zu spät erzeugt wird. D.h. das passiert dann beim Kunden wenn ein Fehler auftritt. Jetzt darfst du dir aussuchen ob du an der Stelle lieber einen Crash hast oder eine fehlende Log-Message.

    Und wenn beides nicht akzeptabel ist gibt es eigentlich nur zwei Möglichkeiten:

    1. Du gibst den Logger explizit überall als Parameter mit.
    2. Du besorgst dir den Logger über eine Funktion die ihn auch erzeugen kann falls er noch nicht existiert. Das wird aber u.U. schwierig wenn das Logging-Zeugs extern konfiguriert werden kann. Denn dann müsstest du dazu ja erstmal die Config lesen & parsen. Und wenn beim Config-Lesen/Parsen Funktionen verwendet werden die wiederrum den Logger anfordern, dann beisst sich der Schwanz in den Hund.

    Ansonsten ist mMn. eine akzeptable Lösung dass man den Logger einfach so früh wie möglich erzeugt. z.B. gleich direkt in main() bevor man noch grossartig andere Dinge macht.
    Wenn man dazu die Config lesen muss, dann kann man die Config lesen & parsen und dann den Logger erzeugen. Dann verwirft man die Config und liest sie neu - damit sämtliche Fehlermeldungen die beim Lesen/Parsen der Config geloggt worden wären halt geloggt werden.

    Je nach Logging-Library kann man u.U. auch Log Messages erstmal nur im RAM puffern lassen. Und dann nach dem Lesen und Anwenden der Config die gepufferten Messages durchgehen und entsprechend rausschreiben lassen.

    Ja, das sehe ich auch so. Das übergeben scheint mir mühselig hat man ja bei vergleichbarem auch nicht z.B. std::cout ... auch global.

    Also std::cout ist jetzt nichts was ich als Vorbild oder Rechtfertigung für irgendwas heranziehen wollen würde.



  • @hustbaer sagte in Auf Garantie für Intialisierungsreihenfolge vertrauen?:

    Wenn du in bestimmten Komponenten/Funktionen nur Fehler loggst (üblich), dann wirst du beim Testen nicht unbedingt merken dass der Logger zu spät erzeugt wird. D.h. das passiert dann beim Kunden wenn ein Fehler auftritt. Jetzt darfst du dir aussuchen ob du an der Stelle lieber einen Crash hast oder eine fehlende Log-Message.

    Das ergibt natürlich Sinn und sollte man wohl im Normalfall beachten. Wobei es in meinem Fall ja sogar bekannt wäre, wo genau der Code crasht. Im Konstruktor der Datenbank. Wenn ich da sage: Ich baue es genau deswegen so um, dass es bei nicht vorhanden sein nicht crasht ... da könnte ich wohl auch einfach die Zeile rauslöschen 🙂 Aber generell ist das natürlich keine schlechte Idee. Bei mir jetzt mittelmäßig relevant, da es ein Privat Projekt ist.

    @hustbaer sagte in Auf Garantie für Intialisierungsreihenfolge vertrauen?:

    Ansonsten ist mMn. eine akzeptable Lösung dass man den Logger einfach so früh wie möglich erzeugt. z.B. gleich direkt in main() bevor man noch grossartig andere Dinge macht.

    Das war auch meine Idee / mein Plan. Ursprünglich habe ich deswegen diese Zeile auch einfach in den Konstruktor meiner Application Klasse geschrieben. Was ich dabei halt eben genau nicht bedacht habe ist das ja noch vor dem Konstruktor die Konstruktoren aller member aufgerufen werden, wo ich eben schon logge möchte.
    Daher fallen mir nur 3 Ansätze ein, um diese Strategie zu fahren:

    • Wie du sagtest, den Logger in die main packen. Das finde ich aber per se auch nicht so schön, da ja der ganze Setup Kram in die Application Klasse sollte.
    • Meine anderen Konstruktruktoren davon befreien etwas loggen zu müssen. Dann müsste ich stattdessen irgendwelche init Methoden verwenden, auch nicht so toll.
    • Dafür sorgen, dass die Konstruktoren später aufgerufen werden, indem ich Pointer / Smartpointer als Member nutze. Geht auch, aber ob das eine valide Begründung dafür ist? Keine Ahnung, vermutlich eher nicht.

    So richtig überzeugen tut mich weder menine Ansätze hier, noch die robusteren wie direkt übergeben. Basierend auf den bisherigen Antworten, scheint es aber auch wohl nicht die perfekte Lösung zu geben.
    Aktuell würde ich dazu tendieren das ganze zu mixen:

    • Übergeben bei den probelematischen Klassen, wo es gleichzeitig noch recht einfach ist (Ist ja auch performanter)
    • Den Logger trotzdem möglichst früh initialisieren
    • Sollte ich den Logger in abgelegen Ecken meines Codes brauchen auf die registry zurückgreifen

    Weil du es grade schon mit Config angesprochen haben. Wie würde ich es mit dem "per Parameter übergeben" Ansatz schaffen die Config auszulesen? Ne statische Methode einführen in der man das dann macht?

    class Application {
       std::shared_ptr<spdlog::logger> logger = getLoggerFromStaticFunction();
       MyDatabaseObjekt myDatabaseObjekt = MyDatabaseObject(logger); 
    }
    

    Das Problem hatte ich tatsächlich auch schon anderer Stelle, wo meine Datenbank Klasse ein Config Objekt erwartet, was man nicht so direkt erzeugen konnte. Da habe ich dann auch ne statische Methode genutzt, um die Config für die Datenbankklasse zu erzeugen. Hatte mich allerdings schon da gefragt, ob das so der richtige Ansatz ist.

    @Mechanics sagte in Auf Garantie für Intialisierungsreihenfolge vertrauen?:

    Man könnte auch einen speziellen Log Appender mocken, der die Nachrichten annimmt, damit man sie im Test dann auswerten kann. z.B. könnte der Test erwarten, dass Log-Nachrichten kommen sollten oder eben nicht.
    Ob das mit spdlog geht, weiß ich nicht.

    Ich muss mich da auch noch mehr einlesen. Bisher habe ich es so verstanden, dass ich eh erstmal ein Interface brauche, um meinen FakeLogger davon erben zu lassen und ihn in den Code einzuschleusen. Das erscheint mir bei nicht selbst geschrieben Code eher schwierig (Wenn man nicht grade einen Wrapper um jede externe Lib schreiben will, was ich auch schon öters gelesen habe ... was mir aber eher weniger spaßig erscheint) Aber ich bin hier auch wie gesagt blutiger Anfänger und habe es vermutlich einfach noch nicht 100% gerafft. Ggf. mache ich dazu nochmal einen Thread auf 😃



  • @Leon0402 sagte in Auf Garantie für Intialisierungsreihenfolge vertrauen?:

    Weil du es grade schon mit Config angesprochen haben. Wie würde ich es mit dem "per Parameter übergeben" Ansatz schaffen die Config auszulesen?

    Kommt auf die Logging-Library an. AFAIK gibt es welche die Makros verwenden, wo man dann u.U. auch nullptr als Logger übergeben kann -- dann wird halt nichts geloggt. In dem Fall einfach die Config-Lese-Methode mit nullptr aufrufen.
    Ansonsten einen Dummy-Logger erzeugen der alle Log-Messages schluckt.

    Da habe ich dann auch ne statische Methode genutzt, um die Config für die Datenbankklasse zu erzeugen. Hatte mich allerdings schon da gefragt, ob das so der richtige Ansatz ist.

    Config würde ich von aussen mitgeben.
    Zum Auslesen kann gerne eine freie Funktion verwendet werden, aber diese würde ich ausserhalb aufrufen und dann die gelesene Config als Parameter mitgeben.



  • @Leon0402 sagte in Auf Garantie für Intialisierungsreihenfolge vertrauen?:

    Ich muss mich da auch noch mehr einlesen. Bisher habe ich es so verstanden, dass ich eh erstmal ein Interface brauche, um meinen FakeLogger davon erben zu lassen und ihn in den Code einzuschleusen.

    Wie man das konkret macht, ist eine andere Frage. Ich hab da eher dran gedacht, den Logger (der immer noch global wäre) im Test zu initialisieren. Das müsste mit jedem Framework irgendwie gehen. Du kannst ja immer konfigurieren, wo die Dateien hingeschrieben werden, wie rotiert wird usw. Genauso kann man oft solche Spielereien implementieren, wie in eine Datenbank zu loggen oder ähnliches. Dafür bietet das Framework irgendwelche Klassen, die z.B. in log4j appender heißen, und die kann man auch selber implementieren.



  • @Leon0402
    Ja.
    Als ich "Logger mocken" geschrieben habe, meinte ich damit nicht notwendigerweise das Ding zu mocken das in der Library die du verwendest als Logger bezeichnet wird. Kann leicht sein dass es für dieses Ding kein "Interface" gibt wo man einen Mock davon ableiten könnte.

    Aber es gibt in jeder Library etwas was sich darum kümmert die Log-Messages rauszuschreiben. Log-Appender, Log-Writer - wie auch immer es heisst. Und da gibt es dann quasi immer so ein Interface - weil's halt quasi immer verschiedene Output-Mechanismen gibt. Da kann man dann für Tests einen Log-Appender/Log-Writer/... implementieren der z.B. einfach in einen vector<string> schreibt. Und dann ggf. auch Assertions darauf machen. Bzw. viele Libraries bringen auch schon einen passenden Log-Appender/Log-Writer/... mit den man dann einfach verwenden kann.



  • @Leon0402 sagte in Auf Garantie für Intialisierungsreihenfolge vertrauen?:

    Daher fallen mir nur 3 Ansätze ein, um diese Strategie zu fahren:

    • Wie du sagtest, den Logger in die main packen. Das finde ich aber per se auch nicht so schön, da ja der ganze Setup Kram in die Application Klasse sollte.
    • Meine anderen Konstruktruktoren davon befreien etwas loggen zu müssen. Dann müsste ich stattdessen irgendwelche init Methoden verwenden, auch nicht so toll.
    • Dafür sorgen, dass die Konstruktoren später aufgerufen werden, indem ich Pointer / Smartpointer als Member nutze. Geht auch, aber ob das eine valide Begründung dafür ist? Keine Ahnung, vermutlich eher nicht.

    Es gibt weitere Möglichkeiten. Du initialisierst den Logger bereits vor main in dem Du eine statische Variable anlegst, die dessen Konstruktion triggert. Dann steht er garantiert vor den Konstruktion Deiner Klassen zur Verfügung. Man muss dann aber aufpassen, dass man nicht in die Problematik der statischen Initialisierungsproblematik rein läuft. Das könnte dann der Fall sein, wenn die Logger Library auch etwas in dieser Richtung macht.

    Die Alternative wäre es ein Meyers Singleton als Wrapper zu nutzen, so dass beim ersten Aufruf des Wrappers der Logger konstruiert wird. Nachteil dabei Meyers Singletons lassen sich nicht sinnvoll gesteuert dekonstruieren.



  • @hustbaer sagte in Auf Garantie für Intialisierungsreihenfolge vertrauen?:

    Zum Auslesen kann gerne eine freie Funktion verwendet werden, aber diese würde ich ausserhalb aufrufen und dann die gelesene Config als Parameter mitgeben.

    Damit man die Klasse besser widerverwenden kann?

    @hustbaer sagte in Auf Garantie für Intialisierungsreihenfolge vertrauen?:

    Aber es gibt in jeder Library etwas was sich darum kümmert die Log-Messages rauszuschreiben. Log-Appender, Log-Writer - wie auch immer es heisst. Und da gibt es dann quasi immer so ein Interface - weil's halt quasi immer verschiedene Output-Mechanismen gibt. Da kann man dann für Tests einen Log-Appender/Log-Writer/... implementieren der z.B. einfach in einen vector<string> schreibt. Und dann ggf. auch Assertions darauf machen. Bzw. viele Libraries bringen auch schon einen passenden Log-Appender/Log-Writer/... mit den man dann einfach verwenden kann.

    Ah okay vielen Dank für den Hinweis, auch an @Mechanics.

    @john-0 sagte in Auf Garantie für Intialisierungsreihenfolge vertrauen?:

    Es gibt weitere Möglichkeiten. Du initialisierst den Logger bereits vor main in dem Du eine statische Variable anlegst, die dessen Konstruktion triggert. Dann steht er garantiert vor den Konstruktion Deiner Klassen zur Verfügung. Man muss dann aber aufpassen, dass man nicht in die Problematik der statischen Initialisierungsproblematik rein läuft. Das könnte dann der Fall sein, wenn die Logger Library auch etwas in dieser Richtung macht.

    Ich glaube dann würde ich es eher in der main machen. Zumindest in meinem Fall hätte ich glaube ich von static nur Nachteile hier

    Was ist. eigentlich von dem Vorschlag zu halten, dass ich z.B. mein DatenbankObjekt Member zu einem (Smart) Pointer mache. Damit könnte ich dann einfach den Logger normal anlegen ... sogar im Konstruktor von main.cpp noch ein "Start Applikation" oder sowas loggen und dann gemütlich danach mein DatenbankObjekt erzeugen. Ich könnte hier dann sogar noch im Konstruktor vorher die Datenbank Config erstellen, die ich direkt übergeben kann (statt dann hier das wieder mit einer freien static Funktion zu machen -> Die Config sollte ja an das Objekt per Parameter übergeben werden und kann halt nicht direkt Konstruiert werden).
    Ist das ein valider Grund hier Pointer zu nutzen (um später initialisieren zu können)? Die dynamische Allokation erzeugt ja Overhead als Nachteil.



  • @Leon0402 sagte in Auf Garantie für Intialisierungsreihenfolge vertrauen?:

    Zum Auslesen kann gerne eine freie Funktion verwendet werden, aber diese würde ich ausserhalb aufrufen und dann die gelesene Config als Parameter mitgeben.

    Damit man die Klasse besser widerverwenden kann?

    Damit man in Unit-Tests eine von Hand zusammengebaute Config mitgeben kann.
    Und gleichzeitig wird die Klasse auch flexibler, ja.



  • @Leon0402 sagte in Auf Garantie für Intialisierungsreihenfolge vertrauen?:

    Die dynamische Allokation erzeugt ja Overhead als Nachteil.

    Beim Zugriff kann es Unterschied geben, der ist aber eher minimal .... aber schlimmer ist der Unterschied beim Erzeugen (Stack/Heap) . Aber wieviele Elemente/Pointer von der Sorte legst du an ? und wann, zur laufzeit in der 90% Schleife ?
    Wenn das einmal in ner Art initialisierung machst, wird es nicht auffallen 🙂 Da wäre mir "Design" wichtiger.

    Generell grad loggen ist ein Thema, wo Singletons mehr Vorteile bringen können als Nachteile 🙂
    Viele Log Frameworks sind um Singletons / statische variablen aufgebaut. Ich hätt da wenig Skrupel das ebenso zu machen.

    Allerdings ist auch das Verlassen auf die Reihenfolge in der Init-Liste keine Todsünde .... in anderen Fällen würde das auch manch kurze elgante Lösung verhindern, wenn man zu paranoid ist.

    Meine Preferenzen: da ich meist Logging Frameworks verwende, hab ich meist das singleton/static soweiso. Und kann dann die anderen Objekte in der main anlegen mit verfügbaren log.

    Ciao ...



  • Einmal zu Beginn wollte ich dynamisch Allokieren. So solls ausssehen:

    class Application {
    public: 
       Logger loger;
       std::unique_ptr<DatabaseClass> databaseObject; 
       std::unique_prt(SomeOtherClass> someOtherClass;
    }; 
    
    Application::Application() 
    : logger {Logger {}}, databaseObject {nullptr}, someOtherClass {nullptr} {
       logger.log("Application ha started"); 
    
       SomeDatabaseConfig config {}; 
       config.setSomething("value"); 
       
       this->databaseObjekt = make_unique<DatabaseClass>(config);
       this->someOtherObject = make_unique<SomeOtherClass>();
    }
    

    Das gefällt mir persönlich recht gut. Dann ist die Reihenfolge sehr eindeutig und ich kann setup wie das Anlegen der Config für die Datenbank auch im Konstruktor machen etc.



  • @Leon0402 sagte in Auf Garantie für Intialisierungsreihenfolge vertrauen?:

    Ich glaube dann würde ich es eher in der main machen. Zumindest in meinem Fall hätte ich glaube ich von static nur Nachteile hier

    Glauben heißt nicht wissen.

    Was ist. eigentlich von dem Vorschlag zu halten, dass ich z.B. mein DatenbankObjekt Member zu einem (Smart) Pointer mache.

    Es ist egal, ob Du da einen unique_ptr (an der Stelle sicherlich sinnvoller als ein shared_ptr) anlegst oder das Problem selbst im Datenbankobjekt löst.Wesentlich ist dann, dass das Objekt mit einem Zustand konstruiert wird, der ein nicht nutzbares Objekt erzeugt, und die Initialisierung verzögert erfolgt. Manchmal lässt sich so etwas nicht vermeiden.

    Damit könnte ich dann einfach den Logger normal anlegen ... sogar im Konstruktor von main.cpp noch ein "Start Applikation" oder sowas loggen und dann gemütlich danach mein DatenbankObjekt erzeugen.

    Ich nehme an, dass Du in der main Funktion ein Objekt anlegen willst, dass die notwendigen Initialisierungen von anderen Objekten durchführt. Das ist nichts anderes als einzige große globale Variable, die als Member alle anderen Dinge aufnimmt. Die Designfrage ist nun, ob Du diese globale Variable durchreichen willst, oder ob Du sie als Singleton umsetzt, so dass man sie nicht explizit durchreichen muss.

    Ist das ein valider Grund hier Pointer zu nutzen (um später initialisieren zu können)? Die dynamische Allokation erzeugt ja Overhead als Nachteil.

    Nein, das kann man so nicht stehen lassen. Allokationen kosten zwar Zeit, aber in Relation etwa zum Aufbau der DB-Verbindung ist das vernachlässigbar. Ein größeres Problem ist die Größe des Stacks, der ist nämlich relativ begrenzt, so dass man da nicht beliebig Objekte erzeugen kann. Deshalb sollte man entweder Objekte mit static Storage (auf x86 gibt es verschiedene Speichermodelle mit dem Standardmodell ist man auf 2GB static begrenzt) oder auf dem Heap erzeugen.



  • @john-0 sagte in Auf Garantie für Intialisierungsreihenfolge vertrauen?:

    Ich nehme an, dass Du in der main Funktion ein Objekt anlegen willst, dass die notwendigen Initialisierungen von anderen Objekten durchführt. Das ist nichts anderes als einzige große globale Variable, die als Member alle anderen Dinge aufnimmt. Die Designfrage ist nun, ob Du diese globale Variable durchreichen willst, oder ob Du sie als Singleton umsetzt, so dass man sie nicht explizit durchreichen muss.

    Ich meinte im Konstruktor von Application und nicht von main. Sorry hatte mich da verschrieben. So hatte ich es vor:

    @Leon0402 sagte in Auf Garantie für Intialisierungsreihenfolge vertrauen?:

    Einmal zu Beginn wollte ich dynamisch Allokieren. So solls ausssehen:

    class Application {
    public: 
       Logger loger;
       std::unique_ptr<DatabaseClass> databaseObject; 
       std::unique_prt(SomeOtherClass> someOtherClass;
    }; 
    
    Application::Application() 
    : logger {Logger {}}, databaseObject {nullptr}, someOtherClass {nullptr} {
       logger.log("Application ha started"); 
    
       SomeDatabaseConfig config {}; 
       config.setSomething("value"); 
       
       this->databaseObjekt = make_unique<DatabaseClass>(config);
       this->someOtherObject = make_unique<SomeOtherClass>();
    }
    

    Das gefällt mir persönlich recht gut. Dann ist die Reihenfolge sehr eindeutig und ich kann setup wie das Anlegen der Config für die Datenbank auch im Konstruktor machen etc.

    Ich bin mir grade nicht sicher, ob das irgendwas an deiner Aussage ändert oder nicht 😃


Anmelden zum Antworten