Verwendung von Rohzeigern in modernem C++ nach C++11 [closed]

Lesezeit: 10 Minuten

Benutzer-Avatar
Gummi jr.

Was sind einige der Hauptgründe für die Verwendung von Rohzeigern im Jahr 2014, da der C++11-Standard jetzt von den meisten anständigen Compilern gut unterstützt wird?

Ich habe ein paar Szenarien identifiziert:

  1. Sie erweitern eine Legacy-Codebasis, die Rohzeiger stark nutzt, und Sie möchten den Stil konsistent halten.

  2. Sie verwenden eine Bibliothek, die nur rohe Zeiger exportiert, aber ich denke, Sie könnten trotzdem Umwandlungen verwenden.

  3. Sie möchten die Fähigkeit von Zeigern ausnutzen, mehrere Indirektionsebenen bereitzustellen. (Ich kenne C ++ 11 nicht gut genug, um zu wissen, ob dies mit intelligenten Zeigern oder mit anderen Techniken erreicht werden kann.)

Welche anderen Szenarien sind Ihrer Meinung nach für die Verwendung von Zeigern geeignet?

Würden Sie heute sogar empfehlen, etwas über Pointer im Allgemeinen zu lernen?

  • An dieser Frage ist überhaupt nichts auszusetzen.

    – Leichtigkeitsrennen im Orbit

    6. September 2014 um 22:42 Uhr

  • Das zugehörige stackoverflow.com/q/22146094 könnte diese Frage ebenfalls behandeln. Aus der Antwort von Joseph Mansfield: „Die zweite [question] wann sollten wir Zeiger verwenden?”

    – dyp

    6. September 2014 um 22:43 Uhr


  • Maslows Hammer: en.wikipedia.org/wiki/Law_of_the_instrument

    – Hans Passant

    6. September 2014 um 22:57 Uhr


  • @FoggyDay: Das Erlernen der Teile von C++, die es mit C teilt, ist eine gute Idee, aber das sollte unbedingt mit einem C++-Compiler erfolgen, nicht mit C. Das C-Typsystem ist ein unsicherer Witz. Teile davon wurden behoben (nicht mehr implizit int), aber Teile sind immer noch eine Einladung zur Katastrophe. Es gibt ein paar C-Funktionen, die nett zu haben sind (namens Initialisierer und restrict), aber sie beginnen nicht, die Probleme zu kompensieren.

    – Ben Voigt

    6. September 2014 um 22:58 Uhr

  • @FoggyDay C++11 ist keine “Monstrosität”. Das Erlernen von Assembler ist kein Muss, um ein guter Programmierer zu werden, es sollte für die meisten (über 90 %) der Menschen keinen Grund geben, Assembler zu lernen. Viele tun das nicht und sie kommen gut zurecht.

    – Raptz

    6. September 2014 um 22:59 Uhr

Benutzer-Avatar
Leichtigkeitsrennen im Orbit

Ich kann mir Situationen vorstellen, in denen Sie ein statisch zugewiesenes Array haben und einen rohen Zeiger verwenden möchten, um es in Hochleistungscode zu durchlaufen. Daran ist noch nichts auszusetzen.

Deine Nummer 1 stimmt.

Ihre Nr. 2 ist möglicherweise nicht richtig: Wenn Sie “Casts verwenden”, um rohe Zeiger, die einer Bibliothek eines Drittanbieters gehören, in intelligente Zeiger umzuwandeln (was lokalen Besitz impliziert), ist etwas schrecklich schief gelaufen.

Ihre Nr. 3 ist technisch gesehen wahr, aber vermeiden Sie dies, wann immer Sie können.

Was ist nicht Heutzutage wird empfohlen, mit rohen Zeigern zu spielen Ihren eigenen, dynamisch zugewiesenen Speicher. Das heißt, der Rat lautet zu vermeiden new ohne intelligente Zeiger (und die Folge davon ist, dass Sie es nicht brauchen sollten delete).

  • +1, besonders letzter Absatz.

    – CodeWütend

    6. September 2014 um 22:46 Uhr

  • Ich denke, Nr. 1 ist meinungsbasiert. Es hört sich so an, als müssten Sie “Konsistenz im Stil” gegen “Vorteile anderer Ansätze” eintauschen.

    – dyp

    6. September 2014 um 22:46 Uhr


  • “Was heutzutage nicht empfohlen wird, ist das Spielen mit rohen Zeigern auf dynamisch zugewiesenen Speicher.” Es ist ein Allzweck-Tool, daher sind spezialisierte Lösungen besser für jedes spezifische Problem geeignet. Es gibt jedoch immer einige Eckfälle. Z.B coliru.stacked-crooked.com/a/a6c607514601b013

    – dyp

    6. September 2014 um 22:50 Uhr

  • Ich sehe nicht, wie “Bibliothek von Drittanbietern Rohzeiger exportiert” gleichbedeutend ist mit “Drittanbieter besitzt das Objekt”. Außerdem ist das der Grund std::unique_ptr unterstützt Löscher, sodass das Objekt zur Bereinigung an die Bibliothek zurückgegeben werden kann.

    – Ben Voigt

    6. September 2014 um 23:03 Uhr

  • Wenn Sie sagen, dass Nr. 3 vermieden werden sollte, liegt das daran, dass es einen besseren Weg gibt, indirekt zu sein, oder einfach, weil es keine Notwendigkeit für indirekte Anweisungen gibt, um Dinge zu erledigen?

    – Gumbly jr.

    6. September 2014 um 23:04 Uhr

Benutzer-Avatar
CodeWütend

Jeder ist gegen rohe Zeiger, da es viel zu einfach ist, sie zu verlieren.

Aber Sie können rohe Zeiger verwenden, um auf Daten zu verweisen, die woanders gehören … tun Sie es einfach nicht new/delete Sie. Verwenden std::unique_ptr oder std::shared_ptr dafür. Oder für dumme (POD) Speicherpuffer verwenden std::vector<unsigned char>nicht malloc und free dich selbst.

Denken Sie an ein std::vector<heavy_object*> wenn Sie mit Unterauswahlen von Objekten jonglieren müssen, die nicht trivial zu kopieren sind, aber bereits anderswo existieren. Dafür brauchen Sie Hinweise.

Sie benötigen auch Zeiger in Funktionen für optionale Argumente, bei denen Referenzen es nicht schneiden, da Sie in der Lage sein möchten, a zu übergeben nullptr.

Auch Zeiger auf aufeinanderfolgende Objekte können einfach iteriert werden ohne irgendetwas std::iterator Overhead. Gerade ++ oder -- und das ist es. Ich verwende stattdessen oft die direkte Pointer-Iteration begin, end für Vektoren.

Wenn Sie verstehen, wie sie funktionieren, werden Sie sie häufig brauchen und richtig einsetzen.

  • Danke, das war hilfreich, guter Punkt zu nullptr.

    – Gumbly jr.

    6. September 2014 um 23:02 Uhr

  • Optionale Argumente sollten sein std::optional<T> heutzutage. Die Einführung einer Indirektion für diese ist ein Antimuster. Ich bin auch neugierig, welchen “Overhead” Ihrer Meinung nach mit Iteratoren verbunden ist.

    – Leichtigkeitsrennen im Orbit

    6. September 2014 um 23:39 Uhr


  • @LightnessRacesinOrbit Ich komme aus einem C-Hintergrund. Wann immer ich kann und Daten in einer Reihe sind, verwende ich Zeiger, um sie zu durchlaufen. Ich glaube nicht, dass sich das in absehbarer Zeit ändern wird. Ich weiß nichts über den Overhead, aber ich weiß, dass Zeiger am wenigsten davon haben … Und bis ich alles weiß, was im C++-Standard passiert, werde ich mich mit C-Stil dem Metall nähern. Ich weiß, dass es die Unsicherheit des minderwertigen Programmierers ist, die mich dazu bringt, aber es hat mir gute Dienste geleistet.

    – CodeWütend

    7. September 2014 um 0:26 Uhr

  • Ihre persönliche Präferenz ist eine Sache, aber zu behaupten, dass Iteratoren einen Overhead haben, wenn Sie offen zugeben, dass Sie keine Ahnung haben, ob dies wahr ist … ist einfach unverantwortlich.

    – Leichtigkeitsrennen im Orbit

    7. September 2014 um 1:24 Uhr

  • In Bezug auf den Vektor: Dies könnte leicht durch einen Vektor von shared_ptr ersetzt werden, oder? Nur damit ich das Konzept richtig hinbekomme

    – Calle Bergström

    4. Oktober 2017 um 7:24 Uhr


Intelligente Zeiger werden zum Handhaben von Objekteigentumsproblemen verwendet, aber nicht alle Zeiger werden verwendet, um Objekteigentum zu handhaben. Zum Beispiel ist es sinnvoller, einen rohen Zeiger an eine Funktion zu übergeben, wenn Sie nicht vorhaben, das Eigentum an dieser Funktion zu übergeben (dh Sie möchten nur, dass die Funktion mit den vom Zeiger adressierten Daten umgeht).

IMHO Raw-Zeiger haben immer noch ihren Platz.

C++11 bietet uns die Möglichkeit, die Lebensdauer von Rohzeigern zu verwalten, sodass wir sie nicht selbst löschen müssen.

Es ist nichts falsch daran, rohe Zeiger zu verwenden, solange sie von einem intelligenten Zeiger/Zeiger-Manager im richtigen Bereich oder Rahmen verwaltet werden, um sicherzustellen, dass ihre Lebensdauer korrekt ist. Wenn dies zutrifft, müssen Sie niemals einen Rohzeiger löschen, und Sie können ihn sicher innerhalb des Bereichs/Rahmens verwenden, in dem seine Lebensdauer garantiert ist.

Ich würde sagen, wenn möglich, speichern Sie einen rohen Zeiger auf Ihr neues Objekt in a std::unique_ptr wenn seine Lebensdauer durch einen bestimmten Umfang/Rahmen gesteuert werden soll. Sobald dies erledigt ist, verwenden Sie den rohen Zeiger innerhalb dieses Bereichsrahmens. Gerade noch nie Lösche es;

Manchmal ist es nicht möglich, die Lebensdauer eines neuen Objekts von einem einzelnen Geltungsbereich oder Rahmen aus zu verwalten. Verwenden Sie in diesem Fall a std::shared_ptr in jedem Scope/Frame, der die Lebensdauer des neuen Objekts eigenständig verwalten muss. Dann gibt es innerhalb jedes Geltungsbereichs/Rahmens keinen Grund, den Rohzeiger nicht zu verwenden, genau wie wenn er von a verwaltet wird std::unique_ptr.

Es gibt also oft keinen Grund, den Geschwindigkeitsnachteil von Smart Pointern in Kauf zu nehmen, da eine ihrer Stärken darin liegt, die Lebensdauer des neuen Objekts zu verwalten, um die Gültigkeit des Raw-Pointers und die automatische Zerstörung seines Objekts sicherzustellen, wenn es ist nicht mehr gebraucht.

Es gibt andere Fälle, in denen ein roher Zeiger nicht geeignet ist.

Zum Beispiel, wenn ein verwalteter Zeiger “Besitz” auf einen anderen Bereich/Frame übertragen muss. In diesem Fall müssen Sie den Bereich/Rahmen ändern, der für die Verwaltung der Lebensdauer des neuen Objekts verantwortlich ist. In diesen Fällen Vermeiden Sie rohe Hinweise wie die Pest!

Welche anderen Szenarien sind Ihrer Meinung nach für die Verwendung von Zeigern geeignet?

Eines der Hauptszenarien, in denen rohe Zeiger verwendet werden, ist, wenn Sie haben nicht besitzen Zeiger. Typischerweise dort, wo eine Referenz funktionieren würde, Sie aber die Einschränkungen einer Referenz vermeiden möchten (nicht wiedereinsetzbar, nicht kopierbar). Sie könnten eine verwenden reference_wrapper geben Sie in diesen Fällen ein, aber es ist einfacher, stattdessen einfach einen rohen Zeiger zu verwenden. Smart-Pointer codieren den Besitz (wer das Objekt erstellt und zerstört). Wenn also kein Besitz zu codieren ist (weil es anders impliziert ist), dann ist ein roher Zeiger in Ordnung.

Nur um es deutlich zu machen, typische Beispiele für das, was ich gerade erklärt habe, sind Dinge wie:

  • temporäre kopierbare Funktoren, die einen Zeiger auf ein Objekt benötigen, das ihm nicht gehört.
  • interne Querverweise innerhalb einer Datenstruktur (z. B. “Rückzeiger”).

Aber es ist wichtig zu beachten, dass diese Dinge im Allgemeinen nicht in Schnittstellen vorhanden sein sollten. Im Allgemeinen können Sie rohe Zeiger in Schnittstellen (z. B. Bibliotheksfunktionen und -klassen) ziemlich vollständig vermeiden und sie nur wirklich intern verwenden, dh im bibliotheksseitigen Code, nicht im benutzerseitigen Code. Mit anderen Worten, wenn Sie rohe Zeiger verwenden müssen, verstecken Sie sie.

Raw-Zeiger werden manchmal auch für optionale Funktionsparameter gesehen, wo Sie ein übergeben können nullptr wenn Sie dieses Ergebnis nicht wollen.

Die Hauptsache, die vermieden werden sollte und im Allgemeinen vermieden werden kann, ist nackt new / delete Aufrufe im benutzerseitigen Code. Eine typische gute moderne C++-Bibliothek (und noch mehr mit C++11) wird keine solche Nacktheit haben new / delete Vorkommnisse, und das ist eine Tatsache.

Raw-Zeiger sind nicht so sehr ein Problem an sich, problematisch ist (1) die manuelle Speicherverwaltung und (2) die Eigentumsverwaltung (was problematisch ist, wenn Raw-Zeiger anstelle von Smart-Pointern verwendet werden).

Würden Sie heute sogar empfehlen, etwas über Pointer im Allgemeinen zu lernen?

Natürlich sollten Sie etwas über Pointer lernen. Sie sind wesentlich für das Verständnis der Programmierung und für das Schreiben von bibliotheksseitigem Code. Raw-Zeiger sind im Innern vieler Bibliothekscodes und dergleichen immer noch sehr präsent, auch wenn Sie sie nicht sehen.

  • Ich stimme den meisten Antworten zu (insbesondere der Betonung auf “nur schlecht für die Speicherverwaltung”), aber 1. std::shared_ptr und std::weak_ptr verschlüsseln Sie nicht das Eigentum. 2. Mit können Sie eine Referenz kopieren int& my_copy = otheroder my_copy(other) in ein Objekt. 3. Ich denke, dass Sie in vielen Fällen, in denen Sie wiedereinsetzbare Referenzen verwenden könnten, iterieren, damit Sie verwenden können for(int& x : thing) stattdessen. Vielleicht ist in den meisten Fällen die Notwendigkeit einer wiedereinsetzbaren Referenz eine Gelegenheit, Code auf höherer Ebene zu schreiben. 4. Für einen optionalen Parameter können Sie manchmal stattdessen einen Standardwert verwenden.

    – leewz

    25. Oktober 2015 um 20:37 Uhr

Benutzer-Avatar
Mann

häufige Gründe, rohe Zeiger von der Spitze meines Kopfes zu verwenden.

  1. Lesen und Analysieren von Binärdateien
  2. Direkte Interaktion mit Hardware
  3. Arrays?
  4. Geschwindigkeit. AFAIK Intelligente Zeiger sind langsamer als rohe Zeiger und es gibt relativ sichere Möglichkeiten, rohe Zeiger zu verwenden. Siehe beispielsweise die Codebasis von Chromium oder die von WebKit. Beide verwenden verschiedene Arten von nachverfolgtem Speicher ohne den vollen Overhead von Smart Pointern. Natürlich auch mit Einschränkungen.

  • Ich stimme den meisten Antworten zu (insbesondere der Betonung auf “nur schlecht für die Speicherverwaltung”), aber 1. std::shared_ptr und std::weak_ptr verschlüsseln Sie nicht das Eigentum. 2. Mit können Sie eine Referenz kopieren int& my_copy = otheroder my_copy(other) in ein Objekt. 3. Ich denke, dass Sie in vielen Fällen, in denen Sie wiedereinsetzbare Referenzen verwenden könnten, iterieren, damit Sie verwenden können for(int& x : thing) stattdessen. Vielleicht ist in den meisten Fällen die Notwendigkeit einer wiedereinsetzbaren Referenz eine Gelegenheit, Code auf höherer Ebene zu schreiben. 4. Für einen optionalen Parameter können Sie manchmal stattdessen einen Standardwert verwenden.

    – leewz

    25. Oktober 2015 um 20:37 Uhr

1090000cookie-checkVerwendung von Rohzeigern in modernem C++ nach C++11 [closed]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy