Ich habe in einigen Artikeln gelesen, dass rohe Zeiger fast nie verwendet werden sollten. Stattdessen sollten sie immer in intelligente Zeiger eingeschlossen werden, unabhängig davon, ob es sich um Gültigkeitsbereichs- oder gemeinsam genutzte Zeiger handelt.
Ich bemerkte jedoch, dass Frameworks wie Qt, wxWidgets und Bibliotheken wie Boost niemals intelligente Zeiger zurückgeben oder erwarten, als würden sie diese überhaupt nicht verwenden. Stattdessen geben sie rohe Zeiger zurück oder erwarten sie. Gibt es dafür einen Grund? Sollte ich mich beim Schreiben einer öffentlichen API von intelligenten Zeigern fernhalten und warum?
Ich frage mich nur, warum intelligente Zeiger empfohlen werden, wenn viele große Projekte sie zu vermeiden scheinen.
c++
api
pointers
smart-pointers
Laurent
quelle
quelle
unique_ptr
? Überhaupt keine. Sind Qt / WxWidgets auf eingebettete Systeme oder Echtzeitsysteme ausgerichtet? Nein, sie sind für Windows / Mac / Unix auf einem Desktop vorgesehen - höchstens. Intelligente Zeiger sind für Programmierer gedacht, die es richtig machen möchten.Antworten:
Abgesehen von der Tatsache, dass viele Bibliotheken vor dem Aufkommen von Standard-Smart-Zeigern geschrieben wurden, ist der Hauptgrund wahrscheinlich das Fehlen einer Standard-C ++ - Anwendungsbinärschnittstelle (ABI).
Wenn Sie eine Nur-Header-Bibliothek schreiben, können Sie intelligente Zeiger und Standardcontainer nach Herzenslust weitergeben. Ihre Quelle steht Ihrer Bibliothek zur Kompilierungszeit zur Verfügung, sodass Sie sich nur auf die Stabilität ihrer Schnittstellen und nicht auf ihre Implementierungen verlassen können.
Aufgrund des Fehlens eines Standard-ABI können Sie diese Objekte jedoch im Allgemeinen nicht sicher über Modulgrenzen hinweg übergeben. Ein GCC unterscheidet
shared_ptr
sich wahrscheinlich von einem MSVCshared_ptr
, der sich ebenfalls von einem Intel unterscheiden kannshared_ptr
. Selbst mit demselben Compiler kann nicht garantiert werden, dass diese Klassen zwischen den Versionen binär kompatibel sind.Unter dem Strich benötigen Sie einen Standard-ABI, auf den Sie sich verlassen können , wenn Sie eine vorgefertigte Version Ihrer Bibliothek verteilen möchten . C hat keine, aber Compiler-Anbieter sind sehr gut in Bezug auf die Interoperabilität zwischen C-Bibliotheken für eine bestimmte Plattform - es gibt De-facto-Standards.
Die Situation ist für C ++ nicht so gut. Einzelne Compiler können die Interaktion zwischen ihren eigenen Binärdateien übernehmen, sodass Sie die Möglichkeit haben, eine Version für jeden unterstützten Compiler zu verteilen, häufig für GCC und MSVC. Vor diesem Hintergrund exportieren die meisten Bibliotheken nur eine C-Schnittstelle - und das bedeutet Rohzeiger.
Nicht-Bibliothekscode sollte jedoch im Allgemeinen intelligente Zeiger gegenüber Raw bevorzugen.
quelle
Es kann viele Gründe geben. Um einige davon aufzulisten:
Bearbeiten : Die Verwendung von intelligenten Zeigern liegt ganz beim Entwickler. Es hängt von verschiedenen Faktoren ab.
In leistungskritischen Systemen möchten Sie möglicherweise keine intelligenten Zeiger verwenden, die Overhead generieren
Für das Projekt, das die Abwärtskompatibilität benötigt, möchten Sie möglicherweise nicht die intelligenten Zeiger verwenden, die über C ++ 11-spezifische Funktionen verfügen
Bearbeiten2 Es gibt eine Reihe von Abstimmungen innerhalb von 24 Stunden, da die Passage unten liegt. Ich verstehe nicht, warum die Antwort abgelehnt wird, obwohl unten nur ein Zusatzvorschlag und keine Antwort angegeben ist.
Mit C ++ können Sie jedoch immer die Optionen öffnen. :) z.B
Und in Ihrem Code verwenden Sie es als:
Für diejenigen, die sagen, dass ein intelligenter Zeiger und ein roher Zeiger unterschiedlich sind, stimme ich dem zu. Der obige Code war nur eine Idee, wo man einen Code schreiben kann, der nur mit a austauschbar
#define
ist. Dies ist kein Zwang .Muss beispielsweise
T*
explizit gelöscht werden, ein Smart Pointer jedoch nicht. Wir können eine Vorlage haben,Destroy()
um damit umzugehen.und benutze es als:
Auf die gleiche Weise können wir einen Rohzeiger direkt kopieren und für einen intelligenten Zeiger eine spezielle Operation verwenden.
Wo
Assign()
ist wie:quelle
std::auto_ptr
die seit langem Teil des Standards sind (und ich möchtestd::auto_ptr
als Rückgabetyp für Funktionen, die Objekte erstellen, auch wenn dies der Fall ist) fast überall sonst nutzlos). In C ++ 11 fallenstd::unique_ptr
keine zusätzlichen Kosten gegenüber einem einfachen Zeiger an.unique_ptr
und Verschwindens vonauto_ptr
Code-Targeting C ++ 03 sollte das spätere verwenden, während Code-Targeting C ++ 11 das erstere verwenden kann. Intelligente Zeiger gibt es nichtshared_ptr
, es gibt viele Standards und keine Standards, einschließlich Vorschläge zum Standard, die alsmanaged_ptr
unique_ptr
Laufzeitkosten sind jedochunique_ptr
bei weitem die am häufigsten verwendeten. Das Codebeispiel Sie bieten auch irreführend, weilunique_ptr
undT*
völlig unterschiedliche Konzepte sind. Die Tatsache, dass Sie beide als bezeichnen,type
erweckt den Eindruck, dass sie gegeneinander ausgetauscht werden können.Es gibt zwei Probleme mit intelligenten Zeigern (vor C ++ 11):
Der standardmäßige Smart Pointer ist insofern kostenlos
unique_ptr
. Leider erfordert es eine C ++ 11-Verschiebungssemantik, die erst kürzlich veröffentlicht wurde. Alle anderen intelligenten Zeiger haben Kosten (shared_ptr
,intrusive_ptr
) oder eine weniger als ideale Semantik (auto_ptr
).Wenn C ++ 11 um die Ecke ist und ein bringt
std::unique_ptr
, wäre man versucht zu glauben, dass es endlich vorbei ist ... Ich bin nicht so optimistisch.Nur wenige große Compiler implementieren den größten Teil von C ++ 11 und nur in ihren neuesten Versionen. Wir können davon ausgehen, dass große Bibliotheken wie QT und Boost bereit sind, die Kompatibilität mit C ++ 03 für eine Weile beizubehalten, was die breite Akzeptanz der neuen und glänzenden intelligenten Zeiger etwas ausschließt.
quelle
Sie sollten sich nicht von intelligenten Zeigern fernhalten, da diese insbesondere in Anwendungen verwendet werden, in denen Sie ein Objekt weitergeben müssen.
Bibliotheken geben entweder nur einen Wert zurück oder füllen ein Objekt. Sie haben normalerweise keine Objekte, die an vielen Orten verwendet werden müssen, sodass sie keine intelligenten Zeiger verwenden müssen (zumindest nicht in ihrer Benutzeroberfläche, sie können sie intern verwenden).
Ich könnte als Beispiel eine Bibliothek nehmen, an der wir gearbeitet haben. Nach einigen Monaten der Entwicklung wurde mir klar, dass wir nur in einigen Klassen Zeiger und intelligente Zeiger verwendeten (3-5% aller Klassen).
Das Übergeben von Variablen als Referenz war an den meisten Stellen ausreichend. Wir verwendeten intelligente Zeiger, wenn wir ein Objekt hatten, das null sein konnte, und rohe Zeiger, wenn eine von uns verwendete Bibliothek uns dazu zwang.
Bearbeiten (ich kann aufgrund meines Rufs keinen Kommentar abgeben): Das Übergeben von Variablen als Referenz ist sehr flexibel: Wenn Sie möchten, dass das Objekt schreibgeschützt ist, können Sie eine konstante Referenz verwenden (Sie können immer noch einige böse Casts ausführen, um das Objekt schreiben zu können ), aber Sie erhalten den größtmöglichen Schutz (dies gilt auch für intelligente Zeiger). Aber ich stimme zu, dass es viel schöner ist, das Objekt einfach zurückzugeben.
quelle
Qt hat viele Teile der Standardbibliothek sinnlos neu erfunden, um Java zu werden. Ich glaube, dass es jetzt tatsächlich seine eigenen intelligenten Zeiger hat, aber im Allgemeinen ist es kaum ein Höhepunkt des Designs. Soweit mir bekannt ist, wurde wxWidgets lange vor dem Schreiben verwendbarer intelligenter Zeiger entwickelt.
Was Boost betrifft, gehe ich davon aus, dass sie, wo immer dies angebracht ist, intelligente Zeiger verwenden. Möglicherweise müssen Sie genauer sein.
Vergessen Sie außerdem nicht, dass intelligente Zeiger vorhanden sind, um das Eigentum durchzusetzen. Wenn die API keine Besitzersemantik hat, warum dann einen intelligenten Zeiger verwenden?
quelle
QString
, wxWidgets hatwxString
, MFC hat den schrecklichen NamenCString
. Ist ein UTF-8 nichtstd::string
gut genug für 99% der GUI-Aufgaben?Gute Frage. Ich kenne die spezifischen Artikel, auf die Sie sich beziehen, nicht, aber ich habe von Zeit zu Zeit ähnliche Dinge gelesen. Mein Verdacht ist, dass die Autoren solcher Artikel dazu neigen, eine Tendenz gegen die Programmierung im C ++ - Stil zu hegen. Wenn der Writer nur dann in C ++ programmiert, wenn er muss, und dann so bald wie möglich zu Java oder Ähnlichem zurückkehrt, teilt er die C ++ - Denkweise nicht wirklich.
Man vermutet, dass einige oder die meisten der gleichen Autoren Speichermanager zum Sammeln von Müll bevorzugen. Ich nicht, aber ich denke anders als sie.
Intelligente Zeiger sind großartig, aber sie müssen die Referenzanzahl beibehalten. Die Führung von Referenzzählungen trägt zur Laufzeit Kosten - oft bescheidene Kosten, aber dennoch Kosten. Es ist nichts Falsches daran, diese Kosten durch die Verwendung von bloßen Zeigern zu sparen, insbesondere wenn die Zeiger von Destruktoren verwaltet werden.
Eines der hervorragenden Dinge an C ++ ist die Unterstützung der Programmierung eingebetteter Systeme. Die Verwendung von bloßen Zeigern ist ein Teil davon.
Update: Ein Kommentator hat korrekt festgestellt, dass C ++ 's new
unique_ptr
(verfügbar seit TR1) keine Referenzen zählt. Der Kommentator hat auch eine andere Definition von "Smart Pointer" als ich denke. Er kann mit der Definition Recht haben.Weiteres Update: Der Kommentarthread unten leuchtet auf. Es wird empfohlen, alles zu lesen.
quelle
shared_ptr
hält eine Referenzzählung. Es gibt viele andere Smart-Pointer-Typen, die überhaupt keinen Referenzzähler haben. Schließlich sind die genannten Bibliotheken auf Plattformen ausgerichtet, die über genügend Ressourcen verfügen. Nicht dass ich der Downvoter gewesen wäre, aber alles was ich sage ist, dass dein Beitrag voller Fehler ist.shared_ptr
hat keinen Overhead. Es hat nur dann Overhead, wenn Sie keine thread-sichere Semantik für gemeinsam genutzte Eigentumsrechte benötigen.Es gibt auch andere Arten von intelligenten Zeigern. Möglicherweise möchten Sie einen speziellen intelligenten Zeiger für eine Netzwerkreplikation (einer, der erkennt, ob auf ihn zugegriffen wird, und Änderungen an den Server oder ähnliches sendet), einen Änderungsverlauf aufzeichnet und die Tatsache markiert, dass auf ihn zugegriffen wurde, damit untersucht werden kann, wann Sie speichern Daten auf der Festplatte und so weiter. Ich bin mir nicht sicher, ob dies die beste Lösung für den Zeiger ist. Die Verwendung der integrierten intelligenten Zeigertypen in Bibliotheken kann jedoch dazu führen, dass Personen an sie gebunden werden und die Flexibilität verlieren.
Menschen können alle möglichen unterschiedlichen Anforderungen und Lösungen für die Speicherverwaltung haben, die über intelligente Zeiger hinausgehen. Ich möchte den Speicher möglicherweise selbst verwalten. Ich kann Speicherplatz für Dinge in einem Speicherpool zuweisen, damit dieser im Voraus und nicht zur Laufzeit zugewiesen wird (nützlich für Spiele). Ich verwende möglicherweise eine durch Müll gesammelte Implementierung von C ++ (C ++ 11 macht dies möglich, obwohl noch keine vorhanden ist). Oder vielleicht mache ich einfach nichts Fortgeschrittenes, um mir Sorgen um sie zu machen. Ich kann wissen, dass ich nicht vergessen werde, nicht initialisierte Objekte zu verwenden und so weiter. Vielleicht bin ich nur zuversichtlich, dass ich das Gedächtnis ohne die Zeigerkrücke verwalten kann.
Die Integration mit C ist ebenfalls ein Problem.
Ein weiteres Problem ist, dass intelligente Zeiger Teil der STL sind. C ++ ist so konzipiert, dass es ohne die STL verwendet werden kann.
quelle
Es hängt auch davon ab, in welcher Domäne Sie arbeiten. Ich schreibe Game-Engines für den Lebensunterhalt. Wir vermeiden Boost wie die Pest. In Spielen ist der Overhead von Boost nicht akzeptabel. In unserer Core Engine haben wir schließlich unsere eigene Version von stl geschrieben (ähnlich wie die ea stl).
Wenn ich eine Formularanwendung schreiben würde, könnte ich die Verwendung intelligenter Zeiger in Betracht ziehen; Aber sobald die Speicherverwaltung zur zweiten Natur geworden ist, wird es ziemlich ärgerlich, keine granulare Kontrolle über den Speicher zu haben.
quelle