it-swarm-eu.dev

Allgemeine Richtlinien zur Vermeidung von Speicherverlusten in C ++

Was sind einige allgemeine Tipps, um sicherzustellen, dass in C++ - Programmen kein Speicherverlust auftritt? Wie finde ich heraus, wer Speicher freigeben soll, der dynamisch zugewiesen wurde?

125
dulipishi

Versuchen Sie, anstatt den Speicher manuell zu verwalten, gegebenenfalls Smart Pointer zu verwenden.
Schauen Sie sich die Boost lib , TR1 und Smart Pointer an.
Auch intelligente Zeiger gehören jetzt zum C++ - Standard C++ 11 .

38
Andri Möll

Ich stimme allen Ratschlägen zu RAII und intelligenten Zeigern uneingeschränkt zu, möchte aber auch einen etwas höheren Hinweis hinzufügen: Der am einfachsten zu verwaltende Speicher ist der Speicher, den Sie nie zugewiesen haben. Im Gegensatz zu Sprachen wie C # und Java, in denen so ziemlich alles eine Referenz ist, sollten Sie in C++ Objekte auf den Stapel legen, wann immer Sie können. Wie ich gesehen habe, weisen mehrere Leute (einschließlich Dr. Stroustrup) darauf hin, dass der Hauptgrund, warum die Garbage Collection in C++ noch nie populär war, darin besteht, dass gut geschriebenes C++ in erster Linie nicht viel Müll produziert.

Schreibe nicht

Object* x = new Object;

oder auch

shared_ptr<Object> x(new Object);

wenn du nur schreiben kannst

Object x;
196
Ross Smith

Verwenden Sie RAII

  • Garbage Collection vergessen (Verwenden Sie stattdessen RAII). Beachten Sie, dass auch der Garbage Collector Daten verlieren kann (wenn Sie vergessen, einige Referenzen in Java/C # auf null zu setzen), und dass der Garbage Collector Ihnen nicht dabei hilft, Ressourcen zu entsorgen (wenn Sie ein Objekt haben, für das ein Handle erworben wurde) Wenn Sie eine Datei erstellen, wird die Datei nicht automatisch freigegeben, wenn das Objekt den Gültigkeitsbereich verlässt, wenn Sie dies nicht manuell in Java tun, oder verwenden Sie das "dispose" -Muster in C #).
  • Vergessen Sie die Regel "one return per function" . Dies ist ein guter C-Rat, um Undichtigkeiten zu vermeiden. In C++ ist er jedoch veraltet, da Ausnahmen verwendet werden (verwenden Sie stattdessen RAII).
  • Und während das "Sandwich-Muster" ein guter C-Rat ist, ist es in C++ veraltet, weil Ausnahmen (use) verwendet werden RAII stattdessen).

Dieser Beitrag scheint sich zu wiederholen, aber in C++ ist das grundlegendste zu erfassende Muster RAII .

Lernen Sie die Verwendung intelligenter Zeiger, sowohl von Boost als auch von TR1 oder sogar von Auto_PTR (aber häufig effizient genug) (aber Sie müssen die Einschränkungen kennen).

RAII ist die Basis sowohl für die Ausnahmesicherheit als auch für die Ressourcenentsorgung in C++, und kein anderes Muster (Sandwich usw.) gibt Ihnen beides (und meistens gibt es Ihnen keines).

Im Folgenden finden Sie einen Vergleich von RAII- und Nicht-RAII-Code:

void doSandwich()
{
   T * p = new T() ;
   // do something with p
   delete p ; // leak if the p processing throws or return
}

void doRAIIDynamic()
{
   std::auto_ptr<T> p(new T()) ; // you can use other smart pointers, too
   // do something with p
   // WON'T EVER LEAK, even in case of exceptions, returns, breaks, etc.
}

void doRAIIStatic()
{
   T p ;
   // do something with p
   // WON'T EVER LEAK, even in case of exceptions, returns, breaks, etc.
}

Über RAII

Zusammenfassend (nach dem Kommentar von Oger Psalm33) Stützt sich RAII auf drei Konzepte:

  • Sobald das Objekt konstruiert ist, funktioniert es einfach! Erwerben Sie Ressourcen im Konstruktor.
  • Objektzerstörung ist genug! Machen Sie im Destruktor Ressourcen frei.
  • Es dreht sich alles um Bereiche! Objekte mit Gültigkeitsbereichen (siehe obiges doRAIIStatic-Beispiel) werden bei ihrer Deklaration erstellt und werden in dem Moment zerstört, in dem die Ausführung den Gültigkeitsbereich verlässt, unabhängig davon, wie der Exit (return, break, exception, etc.).

Dies bedeutet, dass in korrektem C++ - Code die meisten Objekte nicht mit new erstellt werden und stattdessen im Stapel deklariert werden. Und für diejenigen, die mit new konstruiert wurden, wird alles irgendwie sein geltungsbereich (z. B. an einen intelligenten Zeiger angehängt).

Als Entwickler ist dies in der Tat sehr leistungsfähig, da Sie sich nicht um die manuelle Ressourcenbehandlung kümmern müssen (wie in C oder für einige Objekte in Java, das try/finally für diesen Fall) ...

Bearbeiten (2012-02-12)

"Scoped Objects ... werden zerstört ... egal wie der Ausgang ist", das stimmt nicht ganz. Es gibt Möglichkeiten, RAII zu betrügen. Jede Variante von terminate () umgeht die Bereinigung. exit (EXIT_SUCCESS) ist in dieser Hinsicht ein Oxymoron.

- Wilhelmtell

wilhelmtell ist in dieser Hinsicht ganz richtig: Es gibt außergewöhnlich Möglichkeiten, RAII zu betrügen, die alle zu einem abrupten Stopp des Prozesses führen.

Dies sind außergewöhnliche Möglichkeiten, da C++ - Code nicht mit terminate, exit usw. übersät ist. In Ausnahmefällen möchten wir eine nicht behandelte Ausnahme um den Prozess zum Absturz zu bringen und das Speicherabbild des Kerns so zu sichern, wie es ist, und nicht nach der Reinigung.

Aber wir müssen immer noch über diese Fälle Bescheid wissen, da sie zwar selten auftreten, aber dennoch auftreten können.

(Wer ruft terminate oder exit in lässigem C++ - Code auf? ... Ich erinnere mich, dass ich mich mit diesem Problem befassen musste, wenn ich mit [~ # ~] glut [~ # ~] spielte. : Diese Bibliothek ist sehr C-orientiert und entwickelt sie so weit, dass es C++ - Entwicklern schwer fällt, sich nicht um Stack-allokierte Daten zu kümmern oder "interessante" Entscheidungen zu treffen über - kommt nie von der Hauptschleife zurück ... dazu werde ich nichts sagen).

101
paercebal

Sie sollten sich intelligente Zeiger ansehen, z. B. die intelligenten Zeiger von boost .

Anstatt

int main()
{ 
    Object* obj = new Object();
    //...
    delete obj;
}

boost :: shared_ptr wird automatisch gelöscht, sobald der Referenzzähler Null ist:

int main()
{
    boost::shared_ptr<Object> obj(new Object());
    //...
    // destructor destroys when reference count is zero
}

Beachten Sie meine letzte Anmerkung: "Wenn die Referenzanzahl Null ist, ist dies der coolste Teil. Wenn Sie also mehrere Benutzer Ihres Objekts haben, müssen Sie nicht nachverfolgen, ob das Objekt noch verwendet wird. Sobald niemand auf Ihr verweist gemeinsamer Zeiger, es wird zerstört.

Dies ist jedoch kein Allheilmittel. Obwohl Sie auf den Basiszeiger zugreifen können, möchten Sie ihn nicht an eine Drittanbieter-API übergeben, es sei denn, Sie waren mit der Funktionsweise des Zeigers vertraut. Häufig "posten" Sie etwas in einem anderen Thread, damit die Arbeit erledigt werden kann, nachdem der Erstellungsbereich abgeschlossen ist. Dies ist bei PostThreadMessage in Win32 üblich:

void foo()
{
   boost::shared_ptr<Object> obj(new Object()); 

   // Simplified here
   PostThreadMessage(...., (LPARAM)ob.get());
   // Destructor destroys! pointer sent to PostThreadMessage is invalid! Zohnoes!
}

Verwenden Sie wie immer Ihre Denkkappe mit jedem Werkzeug ...

25
Doug T.

Lesen Sie RAII und stellen Sie sicher, dass Sie es verstehen.

12
Hank

Die meisten Speicherverluste sind das Ergebnis von Unklarheiten in Bezug auf Objektbesitz und Lebensdauer.

Das erste, was Sie tun müssen, ist, auf dem Stack zuzuweisen, wann immer Sie können. Dies betrifft die meisten Fälle, in denen Sie ein einzelnes Objekt für einen bestimmten Zweck zuweisen müssen.

Wenn Sie ein Objekt "neu" erstellen müssen, hat es meistens einen einzigen offensichtlichen Eigentümer für den Rest seines Lebens. In dieser Situation verwende ich in der Regel eine Reihe von Sammlungsvorlagen, die dafür konzipiert sind, Objekte zu besitzen, die in ihnen mit einem Zeiger gespeichert sind. Sie werden mit den STL-Vektor- und Kartencontainern implementiert, weisen jedoch einige Unterschiede auf:

  • Diese Sammlungen können nicht kopiert oder zugewiesen werden. (Sobald sie Objekte enthalten.)
  • In diese werden Zeiger auf Objekte eingefügt.
  • Wenn die Sammlung gelöscht wird, wird der Destruktor zuerst für alle Objekte in der Sammlung aufgerufen. (Ich habe eine andere Version, in der behauptet wird, wenn sie zerstört und nicht leer ist.)
  • Da sie Zeiger speichern, können Sie auch geerbte Objekte in diesen Containern speichern.

Mein Vorteil von STL ist, dass es sich so stark auf Value-Objekte konzentriert, während Objekte in den meisten Anwendungen eindeutige Entitäten sind, für deren Verwendung in diesen Containern keine aussagekräftige Kopiersemantik erforderlich ist.

11
Jeroen Dirks

Bah, ihr kleinen Kinder und eure neuen Müllsammler ...

Sehr strenge Regeln zum "Eigentum" - Welches Objekt oder Teil der Software hat das Recht, das Objekt zu löschen. Klare Kommentare und kluge Variablennamen, um zu verdeutlichen, ob ein Zeiger "besitzt" oder "nur gucken, nicht anfassen" ist. Um zu entscheiden, wem was gehört, befolgen Sie so weit wie möglich das "Sandwich" -Muster in jeder Subroutine oder Methode.

create a thing
use that thing
destroy that thing

Manchmal ist es notwendig, an sehr unterschiedlichen Orten zu erschaffen und zu zerstören. Ich denke hart, um das zu vermeiden.

In jedem Programm, das komplexe Datenstrukturen erfordert, erstelle ich einen klaren Baum von Objekten, die andere Objekte enthalten - unter Verwendung von "Eigentümer" -Pointern. Dieser Baum modelliert die grundlegende Hierarchie von Anwendungsdomänenkonzepten. Beispiel: Eine 3D-Szene besitzt Objekte, Lichter und Texturen. Am Ende des Renderns, wenn das Programm beendet wird, gibt es einen klaren Weg, um alles zu zerstören.

Viele andere Zeiger werden nach Bedarf definiert, wenn eine Entität auf eine andere zugreifen muss, um über Felder oder was auch immer zu scannen. Dies sind die "nur suchen". Beispiel für eine 3D-Szene: Ein Objekt verwendet eine Textur, besitzt aber keine. Andere Objekte verwenden möglicherweise dieselbe Textur. Die Zerstörung eines Objekts ruft keine Zerstörung von Texturen hervor .

Ja, es ist zeitaufwändig, aber genau das mache ich. Ich habe selten Speicherlecks oder andere Probleme. Aber dann arbeite ich auf dem begrenzten Gebiet der Hochleistungs-Software für Wissenschaft, Datenerfassung und Grafik. Ich befasse mich nicht oft mit Transaktionen wie im Bank- und E-Commerce-Bereich, mit ereignisgesteuerten GUIs oder mit einem hochgradig vernetzten asynchronen Chaos. Vielleicht haben die neuen Möglichkeiten dort einen Vorteil!

10
DarenW

Gute Frage!

wenn Sie c ++ verwenden und eine Echtzeit-CPU- und Speicher-Boud-Anwendung (wie Spiele) entwickeln, müssen Sie Ihren eigenen Memory Manager schreiben.

Ich denke, das Beste, was Sie tun können, ist, einige interessante Werke verschiedener Autoren zusammenzuführen. Ich kann Ihnen einen Hinweis geben:

  • Überall im Netz wird ein fester Größenverteiler heftig diskutiert

  • Small Object Allocation wurde 2001 von Alexandrescu in seinem perfekten Buch "Modern c ++ design" eingeführt.

  • Eine großartige Weiterentwicklung (mit verteiltem Quellcode) findet sich in einem erstaunlichen Artikel in Game Programming Gem 7 (2008) mit dem Titel "High Performance Heap Allocator", geschrieben von Dimitar Lazarov

  • Eine umfangreiche Liste von Ressourcen finden Sie in this article

Beginnen Sie nicht, selbst einen Noob-Allokator zu schreiben ... DOKUMENTIEREN SIE SICH zuerst.

8
ugasoft

Eine Technik, die bei der Speicherverwaltung in C++ populär geworden ist, ist RAII . Grundsätzlich verwenden Sie Konstruktoren/Destruktoren für die Ressourcenzuweisung. Natürlich gibt es in C++ aus Gründen der Ausnahmesicherheit einige andere unangenehme Details, aber die Grundidee ist ziemlich einfach.

Das Problem hängt im Allgemeinen mit dem Eigentum zusammen. Ich empfehle dringend, die Effective C++ - Reihe von Scott Meyers und Modern C++ Design von Andrei Alexandrescu zu lesen.

5
Jason Dagit

Es gibt bereits viele Möglichkeiten, um Leckagen zu vermeiden. Wenn Sie jedoch ein Tool benötigen, mit dem Sie Leckagen nachverfolgen können, schauen Sie sich Folgendes an:

5
fabiopedrosa

Benutzer-Smart-Zeiger, wo immer Sie können! Ganze Klassen von Speicherlecks verschwinden einfach.

4
DougN

Teilen und kennen Sie die Regeln für den Speichereigentum in Ihrem Projekt. Die Verwendung der COM-Regeln sorgt für die beste Konsistenz ([in] -Parameter gehören dem Anrufer, der Angerufene muss kopieren; [out] -Parameter gehören dem Anrufer, der Angerufene muss eine Kopie erstellen, wenn er eine Referenz aufbewahrt; usw.)

4
Seth Morris

valgrind ist auch ein gutes Werkzeug, um die Speicherverluste Ihres Programms zur Laufzeit zu überprüfen.

Es ist für die meisten Linux-Versionen (einschließlich Android) und für Darwin verfügbar.

Wenn Sie Unit-Tests für Ihre Programme schreiben, sollten Sie sich angewöhnen, Valgrind on-Tests systematisch auszuführen. Es werden möglicherweise viele Speicherverluste in einem frühen Stadium vermieden. Normalerweise ist es auch einfacher, sie in einfachen Tests zu lokalisieren, als in einer vollständigen Software.

Dieser Hinweis gilt natürlich auch für alle anderen Tools zur Speicherüberprüfung.

4
Joseph

Verwenden Sie auch keinen manuell zugewiesenen Speicher, wenn eine Standardbibliotheksklasse (z. B. vector) vorhanden ist. Stellen Sie sicher, dass Sie einen virtuellen Destruktor haben, wenn Sie gegen diese Regel verstoßen.

3
Joseph

Wenn Sie für etwas keinen intelligenten Zeiger verwenden können (obwohl dies eine große rote Flagge sein sollte), geben Sie Ihren Code ein mit:

allocate
if allocation succeeded:
{ //scope)
     deallocate()
}

Das liegt auf der Hand, aber stellen Sie sicher, dass Sie es eingeben vor Sie geben einen Code in den Gültigkeitsbereich ein

2
Seth Morris

Tipps in der Reihenfolge der Wichtigkeit:

-Tipp # 1 Denken Sie immer daran, Ihre Destruktoren als "virtuell" zu deklarieren.

-Tipp 2 Verwenden Sie RAII

-Tipp 3 Verwenden Sie die Smartpointers von boost

-Tipp Nr. 4 Schreiben Sie keine eigenen fehlerhaften Smartpointers, verwenden Sie boost (für ein Projekt, an dem ich gerade arbeite, kann ich boost nicht verwenden, und ich musste meine eigenen intelligenten Pointer debuggen, die ich definitiv nicht nehmen würde die gleiche Route wieder, aber jetzt kann ich unsere Abhängigkeiten nicht steigern)

-Tipp Nr. 5 Wenn etwas Gelegenheitsspiel/Nicht-Performance-kritisch ist (wie in Spielen mit Tausenden von Objekten), schauen Sie sich den Boost-Pointer-Container von Thorsten Ottosen an

-Tipp Nr. 6 Suchen Sie einen Leckerkennungs-Header für die Plattform Ihrer Wahl, z. B. den "vld" -Header von Visual Leak Detection

2
Robert Gould

Eine häufige Ursache für diese Fehler ist, wenn Sie eine Methode haben, die einen Verweis oder Zeiger auf ein Objekt akzeptiert, das Eigentum jedoch unklar lässt. Stil- und Kommentar-Konventionen können dies weniger wahrscheinlich machen.

Der Fall, in dem die Funktion das Eigentum an dem Objekt übernimmt, sei der Sonderfall. Schreiben Sie in allen Situationen, in denen dies geschieht, einen Kommentar neben die Funktion in die Header-Datei, um dies anzuzeigen. Sie sollten sicherstellen, dass in den meisten Fällen das Modul oder die Klasse, die ein Objekt zuweist, auch für die Freigabe verantwortlich ist.

Die Verwendung von const kann in manchen Fällen sehr hilfreich sein. Akzeptieren Sie einen konstanten Verweis, wenn eine Funktion ein Objekt nicht ändert und keinen Verweis darauf speichert, der nach seiner Rückkehr bestehen bleibt. Wenn Sie den Code des Aufrufers lesen, wird deutlich, dass Ihre Funktion das Eigentum an dem Objekt nicht angenommen hat. Dieselbe Funktion hätte auch einen Nicht-Konstanten-Zeiger akzeptieren können, und der Aufrufer könnte annehmen oder nicht, dass der Angerufene den Besitz angenommen hat, aber mit einer Konstantenreferenz gibt es keine Frage.

Verwenden Sie keine Nicht-Konstanten-Referenzen in Argumentlisten. Beim Lesen des Anrufercodes ist sehr unklar, ob der Angerufene einen Verweis auf den Parameter gespeichert hat.

Ich bin mit den Kommentaren nicht einverstanden, die Verweiszähler empfehlen. Dies funktioniert normalerweise einwandfrei, aber wenn Sie einen Fehler haben und dieser nicht auftritt, insbesondere wenn Ihr Destruktor etwas nicht Triviales ausführt, z. B. in einem Multithread-Programm. Versuchen Sie auf jeden Fall, Ihr Design so anzupassen, dass keine Referenzzählung erforderlich ist, wenn es nicht zu schwierig ist.

2
Jonathan

Wenn Sie können, verwenden Sie boost shared_ptr und standard C++ auto_ptr. Diese vermitteln die Besitzersemantik.

Wenn Sie einen auto_ptr zurückgeben, teilen Sie dem Aufrufer mit, dass Sie ihm den Besitz des Speichers geben.

Wenn Sie einen shared_ptr zurückgeben, teilen Sie dem Anrufer mit, dass Sie einen Verweis darauf haben und dass er Teil des Eigentums ist, aber dies liegt nicht ausschließlich in seiner Verantwortung.

Diese Semantik gilt auch für Parameter. Wenn der Anrufer Ihnen einen auto_ptr übergibt, erhalten Sie das Eigentumsrecht.

1
Justin Rudd
  • Vermeiden Sie es, Objekte dynamisch zuzuweisen. Solange Klassen über geeignete Konstruktoren und Destruktoren verfügen, verwenden Sie eine Variable des Klassentyps, keinen Zeiger darauf, und Sie vermeiden eine dynamische Zuordnung und Freigabe, da der Compiler dies für Sie erledigt.
    Eigentlich ist das auch der Mechanismus, der von "intelligenten Zeigern" verwendet wird und von einigen der anderen Autoren als RAII bezeichnet wird ;-).
  • Wenn Sie Objekte an andere Funktionen übergeben, ziehen Sie Referenzparameter Zeigern vor. Dies vermeidet einige mögliche Fehler.
  • Deklarieren Sie nach Möglichkeit Parameter const, insbesondere Zeiger auf Objekte. Auf diese Weise können Objekte nicht "versehentlich" freigegeben werden (außer wenn Sie die const wegwerfen ;-)).
  • Minimieren Sie die Anzahl der Stellen in dem Programm, an denen Sie die Speicherzuweisung und Freigabe vornehmen. Z.B. Wenn Sie denselben Typ mehrmals zuweisen oder freigeben, schreiben Sie eine Funktion dafür (oder eine Factory-Methode ;-)).
    Auf diese Weise können Sie bei Bedarf auf einfache Weise Debug-Ausgaben erstellen (Adressen werden zugewiesen und freigegeben, ...).
  • Verwenden Sie eine Factory-Funktion, um Objekte mehrerer verwandter Klassen einer einzigen Funktion zuzuordnen.
  • Wenn Ihre Klassen eine gemeinsame Basisklasse mit einem virtuellen Destruktor haben, können Sie alle mit derselben Funktion (oder statischen Methode) freigeben.
  • Überprüfen Sie Ihr Programm mit Tools wie purify (leider viele $/€/...).
1
mh.

Wenn Sie Ihren Speicher manuell verwalten, gibt es zwei Fälle:

  1. Ich habe das Objekt erstellt (möglicherweise indirekt, indem ich eine Funktion aufgerufen habe, die ein neues Objekt zuweist), ich benutze es (oder eine Funktion, die ich aufrufe, benutze es), und ich befreie es dann.
  2. Jemand gab mir den Hinweis, also sollte ich ihn nicht befreien.

Wenn Sie gegen eine dieser Regeln verstoßen müssen, dokumentieren Sie diese bitte.

Es geht um Zeigereigentum.

1
Null303

valgrind (nur für * nix-Plattformen verfügbar) ist ein sehr netter Speicherprüfer

1
Ronny Brendel

Andere haben Möglichkeiten zur Vermeidung von Speicherverlusten an erster Stelle erwähnt (z. B. intelligente Zeiger). Ein Profilerstellungs- und Speicheranalysetool ist jedoch häufig die einzige Möglichkeit, Speicherprobleme zu ermitteln, sobald Sie sie haben.

Valgrind memcheck ist eine ausgezeichnete freie.

1
eli

Fügen Sie nur für MSVC Folgendes am Anfang jeder CPP-Datei hinzu:

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

Wenn Sie mit VS2003 oder höher debuggen, werden Sie beim Beenden Ihres Programms über eventuelle Undichtigkeiten informiert (es verfolgt new/delete). Es ist einfach, aber es hat mir in der Vergangenheit geholfen.

1
Rob

C++ wurde unter Berücksichtigung von RAII entwickelt. Es gibt wirklich keinen besseren Weg, um Speicher in C++ zu verwalten, denke ich. Achten Sie jedoch darauf, keine sehr großen Blöcke (wie Pufferobjekte) im lokalen Bereich zuzuweisen. Dies kann zu Stapelüberläufen führen. Wenn bei der Überprüfung der Grenzen während der Verwendung dieses Blocks ein Fehler auftritt, können Sie andere Variablen überschreiben oder Adressen zurückgeben, was zu Sicherheitslücken aller Art führt.

0
artificialidiot

Eines der einzigen Beispiele für das Zuweisen und Zerstören an verschiedenen Orten ist die Thread-Erstellung (der von Ihnen übergebene Parameter). Aber auch in diesem Fall ist es einfach. Hier ist die Funktion/Methode zum Erstellen eines Threads:

struct myparams {
int x;
std::vector<double> z;
}

std::auto_ptr<myparams> param(new myparams(x, ...));
// Release the ownership in case thread creation is successfull
if (0 == pthread_create(&th, NULL, th_func, param.get()) param.release();
...

Hier stattdessen die Thread-Funktion

extern "C" void* th_func(void* p) {
   try {
       std::auto_ptr<myparams> param((myparams*)p);
       ...
   } catch(...) {
   }
   return 0;
}

Ziemlich easyn, nicht wahr? Wenn die Thread-Erstellung fehlschlägt, wird die Ressource von auto_ptr freigegeben (gelöscht), andernfalls wird der Besitz an den Thread übergeben. Was ist, wenn der Thread so schnell ist, dass er nach der Erstellung die Ressource vor dem freigibt?

param.release();

wird in der Hauptfunktion/Methode aufgerufen? Nichts! Weil wir den auto_ptr anweisen, die Freigabe zu ignorieren. Ist die C++ - Speicherverwaltung einfach, nicht wahr? Prost,

Ema!

0
Emanuele Oriani

Verwalten Sie den Arbeitsspeicher auf dieselbe Weise wie andere Ressourcen (Handles, Dateien, Datenbankverbindungen, Sockets ...). GC würde dir auch nicht helfen.

0

Sie können die Speicherzuweisungsfunktionen abfangen und nachsehen, ob einige Speicherbereiche beim Beenden des Programms nicht freigegeben wurden (obwohl dies nicht für alle Anwendungen geeignet ist).

Dies kann auch beim Kompilieren erfolgen, indem die Operatoren new und delete sowie andere Speicherzuweisungsfunktionen ersetzt werden.

Zum Beispiel check in this site [Speicherzuordnung in C++ debuggen] Hinweis: Es gibt einen Trick für den Löschoperator, der auch so aussieht:

#define DEBUG_DELETE PrepareDelete(__LINE__,__FILE__); delete
#define delete DEBUG_DELETE

In einigen Variablen können Sie den Namen der Datei speichern, und wenn der überladene Löschoperator weiß, von welcher Stelle aus er aufgerufen wurde. Auf diese Weise können Sie die Ablaufverfolgung aller Lösch- und Zuordnungsvorgänge in Ihrem Programm anzeigen. Am Ende der Speicherprüfsequenz sollten Sie in der Lage sein, zu melden, welcher zugewiesene Speicherblock nicht "gelöscht" wurde, und ihn anhand des Dateinamens und der Zeilennummer zu identifizieren.

Sie können auch etwas wie BoundsChecker unter Visual Studio ausprobieren, das sehr interessant und einfach zu bedienen ist.

0
INS

Wir umschließen alle unsere Zuweisungsfunktionen mit einer Ebene, an die vorne eine kurze Zeichenfolge und am Ende ein Sentinel-Flag angehängt wird. Sie müssen also beispielsweise "myalloc (pszSomeString, iSize, iAlignment)" oder "new (" description ", iSize) MyObject ()" aufrufen, das intern die angegebene Größe plus genügend Speicherplatz für Ihren Header und Ihr Sentinel zuweist Vergessen Sie nicht, dies für Nicht-Debug-Builds zu kommentieren. Es braucht etwas mehr Speicher, aber die Vorteile überwiegen bei weitem die Kosten.

Dies hat drei Vorteile: Erstens können Sie schnell und einfach nachverfolgen, welcher Code ausläuft, indem Sie schnell nach Code suchen, der in bestimmten "Zonen" zugewiesen, aber nicht bereinigt wurde, wenn diese Zonen freigegeben werden sollten. Es kann auch nützlich sein, zu erkennen, wann eine Grenze überschrieben wurde, indem überprüft wird, ob alle Sentinels intakt sind. Dies hat uns beim Versuch, diese gut versteckten Abstürze oder Array-Fehltritte zu finden, zahlreiche Male erspart. Der dritte Vorteil besteht darin, die Verwendung des Speichers zu verfolgen, um zu sehen, wer die großen Player sind. Eine Zusammenstellung bestimmter Beschreibungen in einem MemDump gibt Aufschluss darüber, wann "Sound" viel mehr Speicherplatz in Anspruch nimmt, als Sie erwartet haben.

0
screenglow