Ich bin auf ein Quiz gestoßen, das eine Array-Deklaration mit unterschiedlichen Größen beinhaltete. Das erste, was mir in den Sinn kam, war, dass ich mit dem new
Befehl eine dynamische Zuordnung verwenden müsste , wie folgt :
while(T--) {
int N;
cin >> N;
int *array = new int[N];
// Do something with 'array'
delete[] array;
}
Ich habe jedoch gesehen, dass eine der Lösungen den folgenden Fall zulässt:
while(T--) {
int N;
cin >> N;
int array[N];
// Do something with 'array'
}
Nach ein wenig Recherche habe ich gelesen, dass g ++ dies zulässt, aber ich habe immer wieder darüber nachgedacht, in welchen Fällen ist es dann notwendig, die dynamische Zuordnung zu verwenden? Oder übersetzt der Compiler dies als dynamische Zuordnung?
Die Löschfunktion ist enthalten. Beachten Sie jedoch, dass es hier nicht um Speicherlecks geht.
c++
arrays
dynamic-memory-allocation
static-memory-allocation
learning_dude
quelle
quelle
std::vector
stattdessen (std::vector<int> array(N);
).new OBJ
direkten Aufruf zuzuweisen .Antworten:
Keines der gezeigten Snippets ist idiomatischer, moderner C ++ - Code.
new
unddelete
(undnew[]
unddelete[]
) sind in C ++ nicht veraltet und werden es niemals sein. Sie sind immer noch die Möglichkeit, dynamisch zugewiesene Objekte zu instanziieren. Da Sie jedoch immer anew
mit adelete
(und anew[]
mit adelete[]
) abgleichen müssen, werden sie am besten in (Bibliotheks-) Klassen aufbewahrt, die dies für Sie sicherstellen. Siehe Warum sollten C ++ - Programmierer die Verwendung von "neu" minimieren? .Ihr erstes Snippet verwendet ein "nacktes"
new[]
und dann niedelete[]
das erstellte Array. Das ist ein Problem.std::vector
macht alles was du brauchst hier ganz gut. Es wird eine Form vonnew
hinter den Kulissen verwendet (ich werde nicht auf Implementierungsdetails eingehen), aber alles, was Sie beachten müssen, ist ein dynamisches Array, aber besser und sicherer.Ihr zweites Snippet verwendet "Arrays mit variabler Länge" (VLAs), eine C-Funktion, die einige Compiler auch in C ++ als Erweiterung zulassen. Im Gegensatz dazu
new
werden VLAs im Wesentlichen auf dem Stapel zugewiesen (eine sehr begrenzte Ressource). Noch wichtiger ist jedoch, dass sie keine Standard-C ++ - Funktion sind und vermieden werden sollten, da sie nicht portierbar sind. Sie ersetzen sicherlich nicht die dynamische (dh Heap-) Zuordnung.quelle
Qt
, da die Basisklassen alle über Garbage Collectors verfügen. Sie verwendennew
sie also meistens und vergessen sie meistens. Wenn für übergeordnete GUI-Elemente das übergeordnete Widget geschlossen wird, verlassen die untergeordneten Elemente den Gültigkeitsbereich und werden automatisch mit dem Müll erfasst.new
noch eine Übereinstimmungdelete
; Es ist nur so, dass diedelete
s vom übergeordneten Widget ausgeführt werden und nicht im selben Codeblock wie dienew
s.Nun, für Vorspeisen,
new
/delete
sind nicht veraltet zu werden .In Ihrem speziellen Fall sind sie jedoch nicht die einzige Lösung. Was Sie auswählen, hängt davon ab, was unter Ihrem Kommentar "Mach etwas mit Array" versteckt wurde.
In Ihrem zweiten Beispiel wird eine nicht standardmäßige VLA-Erweiterung verwendet, die versucht, das Array auf den Stapel zu passen. Dies hat bestimmte Einschränkungen - nämlich eine begrenzte Größe und die Unfähigkeit, diesen Speicher zu verwenden, nachdem das Array den Gültigkeitsbereich verlassen hat. Sie können es nicht herausziehen, es wird "verschwinden", nachdem sich der Stapel abgewickelt hat.
Wenn Ihr einziges Ziel darin besteht, eine lokale Berechnung durchzuführen und die Daten dann wegzuwerfen, funktioniert dies möglicherweise tatsächlich einwandfrei. Ein robusterer Ansatz wäre jedoch, den Speicher dynamisch zuzuweisen, vorzugsweise mit
std::vector
. Auf diese Weise erhalten Sie die Möglichkeit, Platz für genau so viele Elemente zu schaffen, wie Sie benötigen, basierend auf einem Laufzeitwert (was wir die ganze Zeit anstreben), aber es wird sich auch gut bereinigen und Sie können ihn verschieben dieses Bereichs, wenn Sie den Speicher für später verwenden möchten.Wenn Sie zum Anfang zurückkehren,
vector
werden Sie wahrscheinlichnew
ein paar Schichten tiefer gehen, aber Sie sollten sich damit nicht befassen, da die Benutzeroberfläche viel besser ist. In diesem Sinne kann die Verwendung vonnew
unddelete
als entmutigt angesehen werden.quelle
new
und vermeidendelete
, sondern intelligente Zeiger wie verwendenstd::unique_pointer
.std::unique_ptr
std::unique_ptr
Standard-Destruktoraufrufedelete
oderdelete[]
, was bedeutet, dass das eigene Objekt vonnew
oder aufnew[]
jeden Fall zugewiesen worden sein muss, in welche Aufrufestd::make_unique
seit C ++ 14 verborgen wurden .In Ihrem zweiten Beispiel werden Arrays mit variabler Länge (VLAs) verwendet, die eigentlich eine C99-Funktion ( nicht C ++!) Sind, aber dennoch von g ++ unterstützt werden .
Siehe auch diese Antwort .
Beachten Sie, dass Arrays mit variabler Länge sich von
new
/ unterscheidendelete
und diese in keiner Weise "verwerfen".Beachten Sie auch, dass VLAs nicht ISO C ++ sind.
quelle
Modernes C ++ bietet einfachere Möglichkeiten, mit dynamischen Zuordnungen zu arbeiten. Intelligente Zeiger können sich um die Bereinigung nach Ausnahmen (die bei Bedarf überall auftreten können) und vorzeitigen Rückgaben kümmern, sobald die referenzierten Datenstrukturen den Gültigkeitsbereich verlassen. Daher kann es sinnvoll sein, diese stattdessen zu verwenden:
Ab C ++ 14 können Sie auch schreiben
Dies sieht noch besser aus und würde einen Speicherverlust verhindern, wenn die Zuordnung fehlschlägt. Ab C ++ 20 sollten Sie in der Lage sein, so viel wie möglich zu tun
Dies wird für mich zum Zeitpunkt des Schreibens mit gcc 7.4.0 noch nicht kompiliert. In diesen beiden Beispielen verwenden wir auch
auto
anstelle der Typdeklaration links. Verwenden Sie in allen Fällen das Array wie gewohnt:Speicherlecks
new
und Abstürze durch Verdoppelungdelete
sind etwas, das C ++ seit vielen Jahren verprügelt hat und der "zentrale Punkt" der Argumentation für den Wechsel in andere Sprachen ist. Vielleicht besser zu vermeiden.quelle
unique/shared_ptr
Konstruktoren zugunsten von vermeidenmake_unique/shared
, müssen nicht nur den konstruierten Typ nicht zweimal schreiben (mitauto
), sondern Sie riskieren auch nicht, Speicher oder Ressourcen zu verlieren, wenn die Konstruktion teilweise fehlschlägt (wenn Sie einen Typ verwenden, der fehlschlagen kann).make_shared<int[]>
viel, wenn du so ziemlich immer willstvector<int>
, aber gut zu wissen.unique_ptr
Konstruktor ist nothrow, daherT
gibt es nothrow-Konstruktoren, daher besteht kein Risiko für Lecks mitunique_ptr(new int[size])
undshared_ptr
hat Folgendes: "Wenn eine Ausnahme ausgelöst wird, wird delete p aufgerufen, wenn T kein Array-Typ ist, delete [ ] p sonst. ", so haben Sie den gleichen Effekt - das Risiko ist fürunique/shared_ptr(new MyPossiblyAllocatingType[size])
.Neu und Löschen werden nicht veraltet.
Die vom neuen Operator erstellten Objekte können als Referenz übergeben werden. Die Objekte können mit delete gelöscht werden.
Neu und Löschen sind die grundlegenden Aspekte der Sprache. Die Persistenz eines Objekts kann mit new und delete verwaltet werden. Diese werden definitiv nicht veraltet sein.
Die Anweisung - int array [N] ist eine Möglichkeit, ein Array zu definieren. Das Array kann im Rahmen des umschließenden Codeblocks verwendet werden. Es kann nicht so übergeben werden, wie ein Objekt an eine andere Funktion übergeben wird.
quelle
Das erste Beispiel benötigt
delete[]
am Ende ein, sonst tritt ein Speicherverlust auf.Im zweiten Beispiel wird eine variable Arraylänge verwendet, die von C ++ nicht unterstützt wird. Es erlaubt nur einen konstanten Ausdruck für die Array-Länge .
In diesem Fall ist es nützlich,
std::vector<>
als Lösung zu verwenden; Dadurch werden alle Aktionen, die Sie für ein Array ausführen können, in eine Vorlagenklasse zusammengefasst.quelle
Die Syntax sieht aus wie C ++, aber die Redewendung ähnelt der einfachen alten Algol60. Es war üblich, Codeblöcke wie diesen zu haben:
Das Beispiel könnte wie folgt geschrieben werden:
Ich vermisse das manchmal in den aktuellen Sprachen;)
quelle