Ich arbeite an einer mittelgroßen eingebetteten Anwendung in C mit OO-ähnlichen Techniken. Meine "Klassen" sind .h / .c-Module, die Datenstrukturen und Funktionszeigerstrukturen verwenden, um Kapselung, Polymorphismus und Abhängigkeitsinjektion zu emulieren.
Nun würde man erwarten, dass eine myModule_create(void)
Funktion mit einem myModule_destroy(pointer)
Gegenstück kommt. Da das Projekt eingebettet ist, sollten die Ressourcen, die realistisch instanziiert werden, niemals freigegeben werden.
Ich meine, wenn ich 4 serielle UART-Ports habe und 4 UART-Instanzen mit den erforderlichen Pins und Einstellungen erstelle, gibt es absolut keinen Grund, UART # 2 irgendwann zur Laufzeit jemals zerstören zu wollen.
Sollte ich nach dem YAGNI-Prinzip (Du wirst es nicht brauchen) die Destruktoren weglassen? Das scheint mir äußerst seltsam, aber ich kann mir keine Verwendung für sie vorstellen. Ressourcen werden beim Ausschalten des Geräts freigegeben.
quelle
myModule_create(void)
Funktion haben? Sie können die spezifischen Instanzen, die Sie voraussichtlich verwenden, in der von Ihnen bereitgestellten Schnittstelle fest codieren.Antworten:
Glampert ist richtig; Hier werden keine Destruktoren benötigt. Sie würden nur Code aufblähen und eine Gefahr für Benutzer darstellen (die Verwendung eines Objekts nach dem Aufruf seines Destruktors ist undefiniertes Verhalten).
Sie sollten jedoch sicher sein, dass die Objekte wirklich nicht entsorgt werden müssen. Benötigen Sie beispielsweise ein Objekt für einen UART, das derzeit nicht verwendet wird?
quelle
Der einfachste Weg, Speicherlecks zu erkennen, besteht darin, Ihre Anwendung sauber zu beenden. Viele Compiler / Umgebungen bieten eine Möglichkeit, nach Speicher zu suchen, der beim Beenden Ihrer Anwendung noch zugewiesen ist. Wenn einer nicht bereitgestellt wird, gibt es normalerweise eine Möglichkeit, direkt vor dem Beenden Code hinzuzufügen, um dies herauszufinden.
Daher würde ich sicherlich Konstruktoren, Destruktoren und Abschaltlogik bereitstellen, selbst in einem eingebetteten System, das "theoretisch" niemals beendet werden sollte, um die Erkennung von Speicherlecks allein zu vereinfachen. Tatsächlich ist die Erkennung von Speicherverlusten noch wichtiger, wenn der Anwendungscode niemals beendet werden soll.
quelle
In meiner Entwicklung, bei der undurchsichtige Datentypen in großem Umfang verwendet werden, um einen OO-ähnlichen Ansatz zu fördern, habe ich mich ebenfalls mit dieser Frage auseinandergesetzt. Zuerst war ich entschieden im Lager, den Destruktor aus der YAGNI-Perspektive sowie der MISRA-Perspektive "Dead Code" zu eliminieren. (Ich hatte viel Platz für Ressourcen, das war keine Überlegung.)
Das Fehlen eines Destruktors kann jedoch das Testen erschweren, wie beim automatisierten Testen von Einheiten / Integrationen. Herkömmlicherweise sollte jeder Test ein Setup / Teardown unterstützen, damit Objekte erstellt, manipuliert und dann zerstört werden können. Sie werden zerstört, um einen sauberen, unbefleckten Ausgangspunkt für den nächsten Test zu gewährleisten. Dazu benötigt die Klasse einen Destruktor.
Meiner Erfahrung nach ist das "aint't" in YAGNI daher ein "are" und ich habe Destruktoren für jede Klasse erstellt, unabhängig davon, ob ich dachte, ich würde es brauchen oder nicht. Selbst wenn ich das Testen übersprungen habe, gibt es mindestens einen korrekt entworfenen Destruktor für den armen Slob, der mir folgt, der einen hat. (Und zu einem viel geringeren Wert macht es den Code wiederverwendbarer, da er in einer Umgebung verwendet werden kann, in der er zerstört werden würde.)
Während dies YAGNI adressiert, adressiert es keinen toten Code. Dafür finde ich, dass ein bedingtes Kompilierungsmakro wie #define BUILD_FOR_TESTING es ermöglicht, den Destruktor aus dem endgültigen Produktionsbuild zu entfernen.
Gehen Sie dazu folgendermaßen vor: Sie haben einen Destruktor zum Testen / zur zukünftigen Wiederverwendung und erfüllen die Entwurfsziele von YAGNI und die Regeln für "kein toter Code".
quelle
Sie könnten einen No-Op-Destruktor haben, so etwas wie
Stellen Sie dann den Destruktor ein, der
Uart
möglicherweise verwendet wird(Fügen Sie bei Bedarf die geeignete Besetzung hinzu.)
Vergessen Sie nicht zu dokumentieren. Vielleicht willst du sogar
Alternativ kann ein Sonderfall im allgemeinen Code, der den Destruktor aufruft, der Fall sein, in dem die Destruktorzeigerfunktion
NULL
das Aufrufen vermeiden soll.quelle