Frage

Ich erinnere mich, dass der folgende Code nicht C ++ kompatibel ist und hoffte, dass jemand mit vielen C ++ juristischen als ich zu bestätigen, in der Lage wäre, oder es ablehnen.

std::vector<int*> intList;
intList.push_back(new int(2));
intList.push_back(new int(10));
intList.push_back(new int(17));

for(std::vector<int*>::iterator i = intList.begin(); i != intList.end(); ++i) {
  delete *i;
}
intList.clear()

Die Begründung war, dass es illegal ist, für eine Vektor Zeiger auf ungültigen Speicher zu enthalten. Jetzt offensichtlich wird mein Beispiel kompilieren und es wird auch die Arbeit an allen Compilern ich kenne, aber es ist standardkonform C ++ oder soll ich folgende tun, was ich gesagt wurde, in der Tat ist der Standard-konforme Ansatz:

while(!intList.empty()) {
  int* element = intList.back();
  intList.pop_back();
  delete element;
}
War es hilfreich?

Lösung

Der Code ist in Ordnung. Wenn Sie aus irgendeinem Grund über die Elemente besorgt ungültig für einen Moment zu sein, dann den Körper der Schleife ändern

int* tmp = 0;
swap (tmp, *i);
delete tmp;

Andere Tipps

Du Code ist gültig, aber die bessere Lösung sein wird intelligenten Zeiger zu verwenden.

Die Sache ist die, dass alle Anforderungen an std::vector befinden sich in 23.2.4 Abschnitt von C ++ Standards. Im moment gibt es keine Einschränkungen über ungültige Zeiger. std::vector arbeitet mit int* wie bei jeder anderen Art (wir betrachten nicht den Fall von vector<bool>), ist es nicht egal, wo sie sind Punkt.

Die C ++ Philosophie ist es, die Programmierer so viel Spielraum wie möglich zu ermöglichen, und nur Verbot Dinge, die eigentlich vor sich geht, um Schaden verursachen werden. Ungültige Zeiger keinen Schaden an sich selbst, und daher können Sie sie frei haben um. Was wird Schaden anrichten wird der Zeiger in irgendeiner Weise mit, und das ruft daher nicht definiertes Verhalten.

Das ist letztlich eine Frage des persönlichen Geschmacks mehr als alles andere. Es ist nicht „Standards nicht konform“ einen Vektor zu haben, die ungültigen Zeiger enthält, aber es ist gefährlich , so wie es gefährlich ist jeden Zeiger, dass Punkte auf ungültige Speicher zu haben. Ihr letzteren Beispiel wird dafür sorgen, dass Ihr Vektor nie einen schlechten Zeiger enthält, ja, so ist es die sicherste Wahl ist.

Aber wenn Sie wußte , dass der Vektor würde nie während Ihres früheren Beispiels der Schleife verwendet werden (wenn der Vektor lokal scoped ist, zum Beispiel), es ist völlig in Ordnung.

Wo hast du das gehört? Bedenken Sie:

std::vector<int *> intList(5);

Ich habe gerade einen Vektor mit 5 ungültigen Zeigern gefüllt.

Beim Speichern rohe Zeiger in einem Behälter (Ich würde nicht empfehlen), dann einen 2-Phasen-Lösch zu tun hat, würde ich Ihre erste Option über die zweiten wählen.

Ich glaube, Container :: clear () wird der Inhalt der Karte gelöscht werden effizienter als ein einzelnes Element in einer Zeit knallen.

Sie könnten wahrscheinlich die Wende für Schleife in eine schöne (psuedo) forall(begin(),end(),delete) und machen es Generika, so dass es nicht einmal Sache tat, wenn Sie von Vektor zu einem anderen Behälter geändert.

Ich glaube nicht, das ist eine Frage der Einhaltung von Standards. Die C ++ Standards definieren die Syntax der Sprache und Implementierungsanforderungen. Sie verwenden die STL, die eine leistungsfähige Bibliothek ist, aber wie alle Bibliotheken ist es nicht Teil von C ++ selbst ... obwohl ich es vermute, dass, wenn aggressiv verwendet argumentiert werden könnte, Bibliotheken wie STL und Qt die Sprache in eine andere Sprache Obermenge erweitern .

Ungültige Zeiger sind vollkommen kompatibel mit der C ++ Standards, wird der Computer einfach nicht mag es, wenn man sie dereferenzieren.

Was Sie fragen, ist eher eine Frage Best Practices. Wenn Ihr Code ist multi-threaded und intList möglicherweise gemeinsam genutzt wird, dann wird Ihr erster Ansatz sein kann, mehr als gefährlich, aber als Greg vorgeschlagen, wenn Sie, dass intList wissen nicht, dann wird der erste Ansatz zugegriffen werden kann, kann effizienter sein. Das heißt, ich glaube, die Sicherheit in der Regel in einem Trade-off gewinnen sollte, bis Sie wissen, dass es ein Performance-Problem ist.

Wie das Design by Contract Konzept vorgeschlagen, definiert alle Code einen Vertrag, ob implizit oder explizit. Das eigentliche Problem mit dem Code wie das ist, was Sie sich den Benutzer viel versprechend: Vorbedingungen, Nachbedingungen, Invarianten usw. Die Bibliotheken machen einen bestimmten Vertrag und jede Funktion, die Sie schreiben, definiert seinen eigenen Vertrag. Sie müssen nur für Sie Code die richtige Balance holen, und so lange, wie Sie es für den Benutzer (oder sich von sechs Monaten ab jetzt) ??machen klar, was sicher ist und was nicht, wird es in Ordnung sein.

Wenn es Best Practices dokumentiert mit mit einer API, verwenden sie dann, wann immer möglich. Sie sind wahrscheinlich am besten Praktiken für einen Grund. Aber denken Sie daran, eine bewährte Methode im Auge des Betrachters sein kann ..., die sie ist nicht eine bewährte Methode in allen Situationen sein.

  

Es ist illegal für einen Vektor zu enthalten   Zeiger auf ungültige Speicher

Dies ist, was die Norm über den Inhalt eines Behälters zu sagen hat:

(23.3): Die Art der Objekte in diesen Komponenten gespeichert sind, müssen die Anforderungen erfüllen CopyConstructible Typen (20.1.3) und die zusätzlichen Anforderungen von Assignable Typen.

(20.1.3.1, CopyConstructible): In der folgenden Tabelle 30, T ist eine Art von einem C + + Programm Instanziieren eine Vorlage zugeführt werden soll, ist t ein Wert vom Typ T und U ist ein Wert vom Typ const T.

expression  return type  requirement
xxxxxxxxxx    xxxxxxxxxxx  xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
T(t)                       t is equivelant to T(t)
T(u)                       u is equivelant to T(u)
t.~T()      
&t          T*           denotes the address of t
&u          const T*     denotes the address of u

(23.1.4, zuweisbar): 64 T der Typ verwendet wird, den Behälter zu instanziieren, ist t ein Wert von T und U ist ein Wert von (ggf. const) T.

expression  return type  requirement
xxxxxxxxxx    xxxxxxxxxxx  xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
t = u         T&           t is equivilant to u

Das ist alles, das ist, sagt über den Inhalt einer STL-Sammlung. Es sagt nichts über Zeiger und es ist besonders leise über die Zeiger auf gültige Speicher zeigen.

Daher deleteing Zeiger in einem vector, während der meisten wahrscheinlich eine sehr schlechte architektonische Entscheidung und eine Einladung an Schmerz und Leid mit dem Debugger, um 3:00 Uhr an einem Samstag Abend, ist völlig legal.

EDIT:

Kranar Kommentar In Bezug auf das „einen Zeiger auf einen ungültigen Zeigerwert führt zu undefinierten Verhalten zuweisen.“ Nein, das ist falsch. Dieser Code ist perfekt gültig:

Foo* foo = new Foo();
delete foo;
Foo* foo_2 = foo;  // This is legal

Was ist illegal versucht, etwas mit dem Zeiger (oder foo, was das betrifft) zu tun:

delete foo_2; // UB
foo_2->do_something(); // UB
Foo& foo_ref = *foo_2; // UB

Sie einfach einen wilden Zeiger zu schaffen ist legal nach der Norm. Wahrscheinlich nicht eine gute Idee, aber legal dennoch.

EDIT2:

Mehr aus der Norm in Bezug auf Zeigertypen.

So die Standard-sayeth (3.9.2.3):

  

... Ein gültiger Wert eines Objekts Zeiger   Typ stellt entweder die Adresse   ein Byte im Speicher (1.7) oder ein Null   Zeiger (4.10) ...

... und in Bezug auf "ein Byte im Speicher" (1.7.1):

  

Die grundlegende Speichereinheit in der C   + + Speichermodell ist das Byte. Ein Byte ist zumindest groß genug, um zu enthalten,   jedes Mitglied der Grundausführung   Zeichensatz ist und aus einem zusammengesetzt   zusammenhängende Folge von Bits, die   Nummer der   Implementierung definiert. Das Mindeste   Bit heißt die   Bits niedriger Ordnung; die bedeutendsten   Bit ist das Bit hoher Ordnung bezeichnet. Das   Speicher zur Verfügung zu einem C + + -Programm   besteht aus einem oder mehreren Sequenzen von   zusammenhängendes Bytes. Jedes Byte hat ein   eindeutige Adresse.

Es gibt hier nichts über diesen Byte Teil eines lebenden Foo, über Sie Zugang zu ihm haben, oder etwas dergleichen. Es ist nur ein Byte im Speicher.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top