Frage

Ich habe im College gelernt, dass man seine ungenutzten Objekte immer freigeben muss, aber nicht, wie man es tatsächlich macht.Zum Beispiel die richtige Strukturierung Ihres Codes und so weiter.Gibt es allgemeine Regeln für den Umgang mit Zeigern in C++?

Ich darf Boost derzeit nicht verwenden.Ich muss mich an reines C++ halten, da das von mir verwendete Framework jegliche Verwendung von Generika verbietet.

War es hilfreich?

Lösung

Ich habe mit dem eingebetteten Symbian-Betriebssystem gearbeitet, das hierfür ein hervorragendes System bereitstellte, das vollständig auf Entwicklerkonventionen basierte.

  1. Es wird immer nur ein Objekt einen Zeiger besitzen.Standardmäßig ist dies der Ersteller.
  2. Eigentum kann weitergegeben werden.Um die Eigentumsübertragung anzuzeigen, wird das Objekt als Zeiger in der Methodensignatur übergeben (z. B.void Foo(Bar *zonk);).
  3. Der Eigentümer entscheidet, wann das Objekt gelöscht wird.
  4. Um ein Objekt nur zur Verwendung an eine Methode zu übergeben, wird das Objekt als Referenz in der Methodensignatur übergeben (z. B.void Foo(Bat &zonk);).
  5. Klassen, die keine Eigentümer sind, dürfen Verweise (niemals Zeiger) auf Objekte speichern, die ihnen nur dann gegeben werden, wenn sie sicher sein können, dass der Eigentümer sie während der Verwendung nicht zerstört.

Grundsätzlich gilt: Wenn eine Klasse einfach etwas verwendet, verwendet sie eine Referenz.Wenn eine Klasse etwas besitzt, verwendet sie einen Zeiger.

Das hat wunderbar funktioniert und es war eine Freude, es zu benutzen.Gedächtnisprobleme waren sehr selten.

Andere Tipps

Regeln:

  1. Verwenden Sie nach Möglichkeit aintelligenter Zeiger.Boost hat welchedie guten.
  2. Wenn Sie kann keinen intelligenten Zeiger verwenden, null out den Mauszeiger nach dem Löschen.
  3. Arbeiten Sie niemals an einem Ort, an dem Sie Regel 1 nicht anwenden können.

Wenn jemand Regel 1 verbietet, denken Sie daran: Wenn Sie sich den Code einer anderen Person schnappen, die Variablennamen ändern und die Copyright-Hinweise löschen, wird es niemandem auffallen.Es sei denn, es handelt sich um ein Schulprojekt, bei dem tatsächlich mit recht ausgefeilten Werkzeugen nach solchen Spielereien gesucht wird.Siehe auch, diese Frage.

Ich würde hier eine weitere Regel hinzufügen:

  • Erneuern/löschen Sie kein Objekt, wenn ein automatisches Objekt völlig ausreicht.

Wir haben herausgefunden, dass Programmierer, die C++ noch nicht kennen, oder Programmierer, die von Sprachen wie Java kommen, scheinbar etwas Neues lernen und es dann zwanghaft verwenden, wann immer sie ein Objekt erstellen möchten, unabhängig vom Kontext.Dies ist besonders schädlich, wenn ein Objekt lokal innerhalb einer Funktion erstellt wird, nur um etwas Nützliches zu tun.Die Verwendung von „new“ auf diese Weise kann sich negativ auf die Leistung auswirken und dazu führen, dass es nur allzu leicht zu dummen Speicherlecks kommt, wenn der entsprechende Löschvorgang vergessen wird.Ja, intelligente Zeiger können bei Letzterem helfen, aber sie werden die Leistungsprobleme nicht lösen (vorausgesetzt, dass new/delete oder ein Äquivalent hinter den Kulissen verwendet wird).Interessanterweise (naja, vielleicht) haben wir herausgefunden, dass das Löschen bei Verwendung von Visual C++ oft teurer ist als das Neuen.

Ein Teil dieser Verwirrung rührt auch daher, dass von ihnen aufgerufene Funktionen möglicherweise Zeiger oder sogar intelligente Zeiger als Argumente verwenden (wobei Referenzen vielleicht besser/klarer wären).Dies lässt sie denken, dass sie einen Zeiger „erstellen“ müssen (viele Leute scheinen zu glauben, dass new dies tut), um einen Zeiger an eine Funktion übergeben zu können.Dies erfordert natürlich einige Regeln darüber, wie APIs geschrieben werden, um Aufrufkonventionen so eindeutig wie möglich zu gestalten, die durch klare Kommentare, die mit dem Funktionsprototyp geliefert werden, untermauert werden.

Im allgemeinen Fall (Ressourcenverwaltung, bei der es sich bei der Ressource nicht unbedingt um Speicher handelt) müssen Sie mit dem vertraut sein RAII-Muster.Dies ist eine der wichtigsten Informationen für C++-Entwickler.

Vermeiden Sie im Allgemeinen die Zuweisung vom Heap, es sei denn, dies ist unbedingt erforderlich.Wenn nötig, verwenden Sie die Referenzzählung für Objekte, die langlebig sind und von verschiedenen Teilen Ihres Codes gemeinsam genutzt werden müssen.

Manchmal müssen Sie Objekte dynamisch zuweisen, diese werden jedoch nur innerhalb einer bestimmten Zeitspanne verwendet.In einem früheren Projekt musste ich beispielsweise eine komplexe In-Memory-Darstellung eines Datenbankschemas erstellen – im Grunde einen komplexen zyklischen Graphen von Objekten.Allerdings wurde der Graph nur für die Dauer einer Datenbankverbindung benötigt, danach konnten alle Knoten auf einmal freigegeben werden.In einem solchen Szenario ist ein gutes Muster etwas, das ich als "lokales GC-Idiom" bezeichne. Ich bin mir nicht sicher, ob es einen "offiziellen" Namen hat, da ich es nur in meinem eigenen Code und in Cocoa gesehen habe (siehe NSAutoreleasePool in Apples Cocoa-Referenz).

Kurz gesagt: Sie erstellen ein „Sammler“-Objekt, das Zeiger auf die temporären Objekte enthält, die Sie mit „new“ zuweisen.Normalerweise ist es an einen Bereich in Ihrem Programm gebunden, entweder an einen statischen Bereich (z. B.– als stapelzugewiesenes Objekt, das das RAII-Idiom implementiert) oder dynamisch (z. B.– gebunden an die Lebensdauer einer Datenbankverbindung, wie in meinem vorherigen Projekt).Wenn das „Collector“-Objekt freigegeben wird, gibt sein Destruktor alle Objekte frei, auf die es verweist.

Ebenso wie DrPizza halte ich die Einschränkung, keine Vorlagen zu verwenden, für zu streng.Nachdem ich jedoch viel an alten Versionen von Solaris, AIX und HP-UX entwickelt habe (erst kürzlich – ja, diese Plattformen sind in den Fortune 50 noch am Leben), kann ich Ihnen sagen, dass Sie, wenn Ihnen die Portabilität wirklich am Herzen liegt, dies tun werden sollte so wenig wie möglich auf Vorlagen zurückgreifen.Sie für Container und intelligente Zeiger zu verwenden, sollte jedoch in Ordnung sein (bei mir hat es funktioniert).Ohne Vorlagen ist die von mir beschriebene Technik schwieriger umzusetzen.Es würde erfordern, dass alle vom „Kollektor“ verwalteten Objekte von einer gemeinsamen Basisklasse abgeleitet sind.

Tag auch,

Ich würde vorschlagen, die relevanten Abschnitte von „Effective C++“ von Scott Meyers zu lesen.Leicht zu lesen und er deckt einige interessante Fallstricke ab, um Unvorsichtige in die Falle zu locken.

Mich stört auch der Mangel an Vorlagen.Also kein STL oder Boost.Wow.

Übrigens ist es eine ausgezeichnete Idee, Menschen dazu zu bringen, sich auf Konventionen zu einigen.Ebenso wie die Einigung aller auf Konventionen für OOD.Übrigens enthält die neueste Ausgabe von Effective C++ nicht das hervorragende Kapitel über OOD-Konventionen wie die erste Ausgabe, was schade ist, z. B.Konventionen wie die öffentliche virtuelle Vererbung stets modelliert eine „isa“-Beziehung.

rauben

  • Wenn Sie die Verwaltung des Arbeitsspeichers verwenden müssen Stellen Sie manuell sicher, dass Sie delete aufrufen in der gleichen Geltungsbereich/Funktion/Klasse/Modul, die gilt zuerst, z.B.:
  • Lassen Sie den Aufrufer einer Funktion den Speicher zuweisen, der von ihr gefüllt wird, Geben Sie keine neuen Zeiger zurück.
  • Rufen Sie delete immer in derselben exe/dll auf, in der Sie new aufgerufen haben, da es sonst zu Problemen mit Heap-Beschädigungen (verschiedene inkompatible Laufzeitbibliotheken) kommen kann.

Sie könnten alles von einer Basisklasse ableiten, die Smart-Pointer-ähnliche Funktionen implementiert (unter Verwendung der ref()/unref()-Methoden und eines Zählers).

Alle von @Timbo hervorgehobenen Punkte sind beim Entwerfen dieser Basisklasse wichtig.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top