10 Jahre Praxis. Teil 2: Ressourcen

    Guten Tag. Ich hatte vor, einen großen Artikel über Ressourcenmanagement in C ++ zu schreiben.
    In der Praxis ist dieses Thema jedoch so komplex und vielfältig, dass ich auf eine bestimmte Technik eingehen möchte, die ich selbst verwende. Diese Technik ist nicht für alle Fälle eine Rettung, spart aber viel Zeit und Nerven bei der Arbeit mit Objekten. Es ist jedoch nicht allgemein bekannt.

    Dieser Ansatz heißt "alles gehört irgendwo hin". Ich habe es zuerst herausgefunden, als ich von Qt zu dem wunderbaren U ++ Framework gewechselt bin, das von einer Gruppe von Autoren unter der Leitung von Mirek Fídler erstellt wurde. Dies geschah vor ungefähr fünf Jahren, daher werde ich nicht nur die Methode selbst, sondern auch praktische Ratschläge, die auf der Erfahrung mit ihrer Anwendung beruhen, weitergeben.

    Kurz gesagt, das Wesen der Technik lässt sich mit dem Satz beschreiben: "Alles gehört jemandem."
    Die Host-Klasse verfügt über alle Ressourcen, die für die vollständige Arbeit erforderlich sind. Zum Beispiel:
    Objekte, die die Knoten des Trichters beschreiben, sind in der Trichterklasse enthalten. Darin sind Hilfsobjekte sowie Datenobjekte für die komplexe Berechnungsklasse enthalten. Usw.
    Eine Ausnahme bilden einige globale Variablen, bei denen es sich um die grundlegendsten Objekte handelt. In gewisser Weise sind sie Mitglieder einer unbenannten globalen "Klasse".

    Nun sagen wir folgendes:
    1. Ein Objekt wird als reguläres Mitglied einer Klasse definiert oder in einen Container gestellt, der Mitglied einer Klasse ist
    2. Eigentum an der Immobilie wird nicht übertragen

    Hinweis: Es wird nicht als Zeiger definiert, nicht als Link, sondern auf die übliche Weise. Wir können einen Zeiger auf ein Objekt nehmen, es irgendwo übergeben, aber nur zur Verwendung. Das heißt, Code von Drittanbietern kann unseren Zeiger nicht löschen oder neu machen. Es wird nur von der Host-Klasse verwaltet.


    Was ist, wenn das Objekt nicht sofort erstellt wird?
    In diesem Fall benötigen Sie entweder einen einzelnen Container (wie unique_ptr) oder einen Array-Container. Ihre Hauptfunktion ist das automatische Löschen eines Objekts im Destruktor. Das ist alles!

    Und hier halten wir an und überlegen, was wir haben.

    1. Wir haben die manuellen Aufrufe new / delete beseitigt. Und sie haben es so gut beseitigt, dass unsere Ressourcen auch bei Ausnahmefällen von Ausreißern und in anderen Situationen garantiert gelöscht werden.

    2. Wir haben ein sehr gutes automatisches Ressourcenmanagementsystem, das die Semantik des Programms berücksichtigt.
    Zum Beispiel haben wir mehrere Fenster. Einige der Fenster im Verlauf des Programms werden zerstört. Dadurch werden alle Ressourcen entfernt. Wir kontrollieren genau, wann die Ressource gelöscht wird. Dies ist sehr wichtig.
    Wir verlassen uns nicht auf den Müllmann. Wir wissen mit Sicherheit, dass im richtigen Moment der Speicher freigegeben wird.

    Sie müssen zugeben, dass deterministisches Ressourcenmanagement ein großer Kontrast zu der Fähigkeit ist, jederzeit auf den Löschvorgang zu stoßen (z. B. während der Wiedergabe des Videos, oder Sie müssen den Servoantrieb stoppen) oder die Fähigkeit, den Garbage Collector vergeblich zu schütteln, um die Ressource zum richtigen Zeitpunkt zu löschen .

    3. Wenn das Host-Objekt einen Zeiger auf sein Mitglied ausgibt, kann es sicherstellen, dass durch diesen Zeiger das erforderliche Objekt der gewünschten Klasse vorhanden ist. Wenn wir einen solchen Zeiger verwenden, arbeiten wir im Bereich des Host-Objekts. Das heißt, wir sind die Gültigkeit des Zeigers garantiert. Verstehst du Die Lebensdauer des Objekts durch den Zeiger wird vom Compiler selbst garantiert, der beim Kompilieren die Sichtbarkeit des Host-Objekts überprüft.
    Es ist fast ein Traum: effektiv durch Zeiger zu arbeiten, deren Gültigkeit bei der Kompilierung überprüft wurde! Und das alles - ohne Overhead.

    4. Als wir schließlich feststellten, dass das Problem des Erstellens, Löschens und der Gültigkeit des Zugriffs ohne Mehraufwand gelöst werden konnte, kamen wir zu dem Schluss, dass „komplexe“ intelligente Zeiger mit einem Referenzzähler und komplexeren internen Mechanismen einfach nicht benötigt wurden.

    Wir beschränken die Struktur des Programms. Wir führen eine gründlichere Planung durch. Und so kommen wir zu einer wesentlich deterministischeren Arbeit mit Ressourcen ohne Overhead. Wir verwerfen alle expliziten Aufrufe zum Löschen, die meisten expliziten Aufrufe für neue und Container mit Referenzzählern - und vermeiden alle damit verbundenen Probleme.

    Ein solches Programm unterscheidet sich qualitativ von dem Programm in C. Hier verstecken wir uns vor allem, was für die aktuelle Funktionalität nicht relevant ist. In den meisten Fällen haben wir nicht Dutzende globaler Variablen oder Objekte und Hunderte globaler Funktionen, die ihnen dienen. Stattdessen gibt es normalerweise mehrere globale Objektvariablen. Jedes von ihnen ist ziemlich autonom und implementiert eine ausreichend große Funktionalität, die von den enthaltenen Objekten implementiert wird. Im letzten Artikel habe ich erwähnt, dass es wünschenswert ist, diese Objekte zu privaten Mitgliedern zu machen , da dies die Verkettung innerhalb der Klassenhierarchie minimiert.

    Nun, kurz gesagt, wie sieht der Ansatz aus?

    Nun zu den Fallstricken.
    Dies kann zuallererst eine völlig andere Art der Zerlegung in Klassen bedeuten als der Mainstream-Ansatz, bei dem intelligente Zeiger mit einem Referenzzähler aktiv verwendet werden. Oft ist es sehr schwierig, diese Beschränkungen aufzuerlegen, und der Ansatz mit intelligenten Zeigern scheint attraktiver zu sein. Aber, wie die Praxis der Entwickler des Frameworks und die Praxis des Autors des Artikels zeigen, ist es besser, alles in Übereinstimmung mit dem Ansatz zu versuchen. Diese Bemühungen werden sich beim Debuggen und Unterstützen des Programms zehnmal auszahlen.

    Zweitens benötigen Sie bei aktiver Verwendung gute, schnelle Container, die den Elementklassen keine Einschränkungen in Form von Kopieroperatoren auferlegen. Die einfache Implementierung solcher Container ist denkbar einfach. Effektive Implementierung ist ein separater Artikel. Denn für eine effektive Arbeit müssen Sie die Übertragung (es ist auch destruktives Kopieren) sowie die Übertragung (es ist auch ultraschnelle Kopie) implementieren - wenn es interessant ist, werden diese Themen weiter besprochen.

    Drittens kann sich die folgende GUI-Strategie aus diesen Regeln ergeben. Die Steuerelemente gehören ihrem logischen Eigentümer (nicht dem Fenster, sondern demjenigen, der seine Semantik in ihnen speichert, weil er sein Eigentümer ist). In diesem Sinne haben Sie zwei Möglichkeiten: MVC-Apologeten können die Dinge so lassen, wie sie sind. Oder Sie können diesen Ansatz wählen, der sich, wie die Praxis zeigt, oft bezahlt macht. Dies ist natürlich keine heilige Kuh, sondern wird in der Praxis kritisch reflektiert und überprüft.

    Bitte machen Sie auf Ungenauigkeiten aufmerksam, versuchen Sie es, kritisieren Sie, bewerben Sie sich. Ich freue mich über jede Rückmeldung.

    Referenzen:
    1. U ++ Übersicht .

    Der nächste Teil soll die Auswahl von schönen und beweglichen Objekten behandeln. Es wird gezeigt, wie diese Funktionen die Geschwindigkeit beim Arbeiten mit Objekten erheblich steigern. Und darauf basierende Container sind 4-5 mal schneller als STL .
    Danach wird es möglich sein, ein von Erlang inspiriertes Multithreading mit Turbo-Geschwindigkeit und sehr sicher umzusetzen.

    Jetzt auch beliebt: