wake-up-neo.net

Wie können Sie feststellen, ob ein Zeiger gelöscht wurde, und ihn sicher löschen?

In C++ Wie kann man entscheiden, ob ein Zeiger zuvor gelöscht wurde?

als ich versuchte, einen Zeiger zu löschen, der zuvor in einem anderen Teil des Codes gelöscht wurde, löste er eine Ausnahme aus, die nicht behandelt werden konnte.

Ich habe mich gefragt, ob es eine Möglichkeit gibt, den Zeiger zu überprüfen oder zu löschen? jede Referenz zu erweiterten Speicheroperationen.

ich möchte auch die nicht behandelten Ausnahmen von Zeigern beherrschen und den Zugriff auf geschützt oder Zugriff ist Verletzung, ... diese Art von Fehler.

danke für diejenigen, die etwas von ihrem Wissen und ihrer Zeit geben, um anderen Menschen zu helfen und ihre Vorzüge zu teilen


Aktualisieren

Der große Rat vieler moderner C++ - Entwickler-Community lautet: Verwenden Sie intelligente Zeiger oder versuchen Sie, die Verwendung von Rohzeigern zu vermeiden. Aber für die Sicherheit beim Wurf und für die Sicherung ohne Arbeitsspeicher ( ISO_CPP_FAQ ) und natürlich, wenn Sie den geringen Aufwand für die Verwendung von intelligenten Zeigern vermeiden möchten [möglicherweise nicht immer auffällig, aber sie haben Overhead], können Sie Ihre benutzerdefinierten Methoden schreiben mit rohen Zeigern [Typ *] - dies ist nicht allgemein. Bevorzugen Sie immer intelligente Zeiger auf Rohzeiger.

In 'Going Native 2013' wurde ein allgemeiner Rat gegeben: Verwenden Sie niemals rohe Zeiger.

20
ahmedsafan86

Es gibt drei Lösungen. Möglicherweise möchten Sie je nach Aufwand/Qualitätsverhältnis, das Sie erzielen möchten, eines auswählen: 

Elegante und richtigste Lösung: 

Verwenden Sie intelligente Zeiger, und Sie müssen delete nicht mehr manuell aufrufen. Dies ist der beste Weg, um dieses Problem zu überwinden. Es verwendet das Prinzip von RAII, das perfekt für eine Sprache wie C++ funktioniert, die keinen eingebauten Garbage-Collector hat. 

Weniger elegante, aber praktikable Lösung: 

Weisen Sie den Zeiger nach dem Löschen NULL zu. Das Aufrufen von delete auf einem NULL-Zeiger ist ein No-Op, so dass die zusätzliche NULL-Prüfung entfällt, dies kann jedoch durch hide einige Probleme verursacht, anstatt sie sichtbar zu machen. 

Weniger elegante, aber richtigere Lösung: 

Beseitigen Sie alle delete-Probleme, indem Sie Ihr Programm zum Absturz bringen. Sie können auch Speicheranalyseprogramme wie valgrind verwenden und dann Ihren Code korrigieren, um all diese Probleme zu vermeiden.

25
Alok Save

Dies ist eine gute Frage, aber eine der grundlegenden Wahrheiten bei der Arbeit in einer manuell verwalteten Umgebung (wie C/C++ und seinen Verwandten) besteht darin, dass es keinen guten Weg gibt, einen Zeiger nach der Tatsache zu fragen, ob es ist gültig - sobald es ungültig geworden ist, ist es verschwunden, und wenn man es betrachtet, neigt es zum Sprengen. Ihre Aufgabe ist es sicherzustellen, dass sie nie mehr als einmal gelöscht oder freigegeben wird und danach nicht mehr darauf zugegriffen wird.

Schauen Sie sich auf jeden Fall die intelligenten Zeiger an, die erfunden wurden, um Programmierern das Leben unter diesen Umständen zu erleichtern. (Die traditionellere Methode ist, vorsichtig zu sein, es nicht zu vermasseln, und dann dem Zeiger möglicherweise NULL zuzuweisen, wenn Sie wissen, dass er gelöscht wurde, wie Alok sagt.)

4
Ben Zotto

In C++ Wie kann man entscheiden, ob ein Zeiger zuvor gelöscht wurde?

Der Sprachstandard bietet keine legale Möglichkeit, festzustellen, ob ein beliebiger Zeiger gültig ist oder nicht.

Es gibt einen Weg, aber es ist sehr spezifisch für das Compiler/Betriebssystem. Sie können sich entweder in den vorhandenen Speichermanager einhaken oder durch Ihren eigenen ersetzen und eine dedizierte Funktion für die Zeigerüberprüfung bereitstellen. Es kann jedoch nicht ganz einfach sein. Sie möchten sich auf diese Funktionalität nicht wirklich verlassen, wenn die Leistung von entscheidender Bedeutung ist.

3
Alexey Frunze

Der Zeiger sagt Ihnen nichts. Ihr Design sollte: Wenn Sie dynamische Zuweisung verwenden, ist es normalerweise, weil Ihre -Anwendung erfordert, dass das Objekt eine bestimmte Lebensdauer hat, so dass Sie wissen, wann das Objekt korrekt gelöscht wird. Wenn das Objekt kopiert werden kann oder eine Lebensdauer hat, die dem Gültigkeitsbereich entspricht, weisen Sie es (normalerweise) nicht dynamisch zu. 

Es gibt natürlich Ausnahmen in sehr unterem Code. Wenn Sie etwas wie std::vector implementieren, müssen Sie eine dynamische Zuweisung verwenden, da die Größe unter Nicht bekannt ist Zeit kompilieren. Solche Zuordnungen sollten jedoch nicht entgehen. Die Verantwortung für den Umgang mit dem Speicher __ liegt in der untergeordneten Klasse. 

Pufferüberläufe beim Zugriff auf bereits gelöschten Speicher und Sind schließlich undefiniertes Verhalten. Sie führen im Allgemeinen nicht zu einer Ausnahme, und es gibt keine generische Art, wie sie behandelt werden. (Sie können normalerweise veranlassen, ein Signal zu erhalten, wenn Solche Dinge vorkommen, aber es gibt so wenige Dinge, die Sie mit einem -Signalhandler tun können, das hilft nicht viel.) Im Allgemeinen Sie möchten, dass das Programm abstürzt, da Sie nicht wissen, in welchem ​​Zustand es ist. In den seltenen Fällen, in denen dies nicht der Fall ist, müssen Sie auf die definierte Implementierung zurückgreifen Erweiterungen, falls vorhanden. Wenn Sie mit der Option /EHa Beispielsweise mit VC++ kompilieren, wird ein normalerweise ohnehin abstürzender Code in eine C++ - Ausnahme umgewandelt. Dies ist jedoch eine Erweiterung von VC++ , Und Sie wissen immer noch nicht den Gesamtstatus des Programms , Wenn dies der Fall ist. Wenn Sie die free-Weltraumarena beschädigt haben, können Sie wahrscheinlich nicht viel tun, auch wenn Sie die Ausnahme abfangen (und es besteht eine gute Chance, dass Eine weitere Ausnahme von einem Destruktor versucht wird um Speicher freizugeben, wenn Sie den Stapel abwickeln).

2
James Kanze

verwenden Sie shared_ptr<> und shared_array<>. Denken Sie daran, shared_ptr<> kann verwendet werden, um den einem Array zugewiesenen Speicher nur zu verwalten, wenn ein entsprechender Deleter vorhanden ist. Andernfalls verwenden Sie shared_array<> zur Verwaltung Ihrer Arrays

A* a_tab=new A[100];
boost::shared_ptr<A> a_tab_ok(a_tab,ArrayDeleter<A>()); 

// nur ok wenn

template <typename T>
    class ArrayDeleter
    {
    public:
        void operator () (T* d) const
        {
            delete [] d; //will delete array!
        }
    };

wird gestellt

2
4pie0

Ich weiß, dass dieser Thread alt ist. Aber wenn jemand anderes dies liest, sollte er über unique_ptr Bescheid wissen. shared_ptr hat in der Tat einen Overhead. Der Zähler wird auf dem Heap gespeichert. Bei jedem Zugriff auf den Zähler besteht die Gefahr, dass der Prozessor-Cache nicht übereinstimmt. unique_ptr Ist eingeschränkter, hat jedoch keinen Overhead im Vergleich zu einfachen Zeigern. Mein Vorschlag ist, unique_ptr gegenüber shared_ptr zu bevorzugen, wenn Sie keine Referenzzählung benötigen . Ein weiterer wichtiger Hinweis ist, dass unique_ptr gut mit Arrays funktioniert.

1
Martin Fehrs

Intelligente Zeiger sind eine bessere Wahl, um solche Probleme zu vermeiden (Sie müssen jedoch vor deren Verwendung auch ein umfassendes Verständnis haben), ich möchte jedoch die mit Smart-Zeigern verbundenen Leistungsbeschränkungen erwähnen. Grund dafür ist, dass sie normalerweise atomare Operationen verwenden, z Zählen. Diese Funktionen sind wesentlich langsamer als die ganzzahlige Arithmetik. Ich bin mir nicht sicher, ob eine solche kleine Leistungsstrafe in Ihrem Fall akzeptabel ist oder nicht.

Was ich normalerweise mache (also muss ich nicht erst später Tage damit verbringen, böse Bugs zu debuggen), verbringe ich viel Zeit mit dem Design und der Objektlebensdauer, bevor ich mich für die eigentliche Codierung bewege, da ich Speicher lösche, auf den ich speziell den Zeiger setze NULL, es ist gute Praxis, soweit ich denke. Vielleicht ist es auch tatsächlich die Lösung, mehr Zeit darauf zu verwenden, Abhängigkeiten und die Lebensdauer des Objekts zu bestimmen, bevor es weitergeht! 

1
Saqlain