2012-09-17 17 views
5

Ich habe ein paar Projekte, die boost::shared_ptr oder std::shared_ptr ausgiebig verwenden (Ich kann zu jeder Implementierung bald genug konvertieren, wenn es eine gute Antwort auf diese Frage gibt für den einen, aber nicht den anderen). Die Boost-Implementierung verwendet Boost.Assert, um die Rückkehr zu vermeiden, wenn während der Laufzeit ein leerer Zeiger (NULL) in operator* oder operator-> angetroffen wird; während die libC++ -Implementation scheint keine Überprüfung zu haben.Anpassen von std :: shared_ptr oder boost :: shared_ptr um eine Ausnahme auf NULL Dereferenz zu werfen

Während natürlich die Gültigkeit einer shared_ptr sollte vor der Verwendung überprüft werden, führt eine große, gemischt-Paradigma Codebasis mich zu einer Ausnahme-werfen Variante versuchen wollen; Der größte Teil des Codes ist relativ ausnahmebedingt und wird allenfalls auf einen hohen, aber fortsetzbaren Zustand stoßen, anstatt auf std::terminate() oder segfault.

Wie sollte ich diese Accessoren am besten anpassen und gleichzeitig die Robustheit von shared_ptr beibehalten? Es scheint, dass die Kapsel in einer throwing_shared_ptr die beste Option ist, aber ich bin vorsichtig, die Magie zu brechen. Bin ich am besten dran, die Boost-Quelle zu kopieren und einfach die ASSERT s in eine entsprechende throw-Anweisung zu ändern?


Der tatsächliche Typ Name überall für den entsprechenden smart_ptr<T> Typ verwendet wird, ist ein typedef aus einem Makro erweitert; das heißt ForwardDeclarePtr(Class) erweitert um so etwas wie:

class Class; 
typedef boost::smart_ptr<Class> ClassPtr; 

Alles vergeht, nimmt, oder speichert eine ClassPtr - so kann ich den zugrunde liegenden Typ ziemlich frei ersetzen; und ich vermute, dass dies das potenzielle Problem des Slicing/Hiding mildert.

Antwort

5

Es gibt wirklich keine "Magie" in std::shared_ptr<T>, die entfernt würde, wenn Sie es in eine benutzerdefinierte Klasse, die eine Ausnahme beim Dereferenzieren eines freigegebenen NULL-Zeiger würde eine Ausnahme werfen würde. Ich sehe also nicht, warum dieser Ansatz nicht funktioniert, solange Ihre neue Wrapper-Klasse der Semantik des Typs std::shared_ptr<T> folgt.

BTW, Sie könnten auch einen etwas anderen Ansatz, und das ist eine Wrapper-Klasse erstellen, die einfach nicht zulassen, dass andere NULL Zeiger auf die umschlossenen std::shared_ptr<T> Daten Mitglied an erster Stelle übergeben. Im Grunde wäre es eine Klasse, die das std::make_shared<T> Idiom in seinem Konstruktor erzwingen würde. Ich bin mir nicht sicher, basierend auf der Funktionsweise Ihres Codes, wenn dies möglich ist, aber es ist eine andere Möglichkeit, das Problem zu umgehen, indem Sie einen RAII-Ansatz verwenden und keine Ausnahmen auslösen.

+0

Diese Antwort zu akzeptieren, weil es ein bisschen einfacher ist, das Design, die Sicherheit und die Verwendung der Kapselung in Kommentaren zu erklären (Klassen haben die 'shared_ptr'-Mitglieder die ganze Zeit). @ KevinBallard ist auch richtig, though. – rvalue

5

Nur Unterklasse std::shared_ptr in throwing_shared_ptr, überschreiben diese beiden Methoden, und haben sie durch und rufen bis std::shared_ptr impl. Dies sollte gut funktionieren, solange Sie throwing_shared_ptr überall verwenden, anstatt es auf std::shared_ptr zu schneiden.

+0

Selbst das Schneiden ist kein katastrophales Problem, solange der abgeleitete Typ keine Datenelemente hinzufügt. –

+0

@MarkRansom: Richtig, aber es wird die Wurfart entfernen. Natürlich, wenn Ihr Wert als 'std :: shared_ptr 'eingegeben wird, dann denke ich, dass Sie das sowieso erwarten sollten. –

+0

@KevinBallard Dies könnte tatsächlich das größere Problem als die Frage sein; was zu tun ist, wenn etwas eine einfache 'shared_ptr' erwartet - es gibt mindestens einen Fall von 3rd-Party-Code, der dies tut.Ich bezweifle, eine Ausnahme durch diese Bibliothek zu werfen, wäre * schlimmer * als 'std :: terminate()' oder 'SIGSEGV' - aber es lohnt sich, ernsthaft darüber nachzudenken. – rvalue

Verwandte Themen