2010-02-05 13 views
126

Ich fange gerade an, in C++ zu kommen, und ich möchte einige gute Gewohnheiten aufheben. Wenn ich gerade ein Array vom Typ int mit dem Operator new zugewiesen habe, wie kann ich sie alle auf 0 initialisieren, ohne sie alle selbst zu durchlaufen? Sollte ich einfach memset verwenden? Gibt es einen “ C++ ” Weg, es zu tun?Wie Speicher mit neuem Operator in C++ initialisieren?

+15

Wenn Sie eine gute C++ - Angewohnheit aufnehmen wollen, dann vermeiden Sie Arrays direkt und verwenden Sie stattdessen Vektor. Vector initialisiert alle Elemente unabhängig vom Typ, und Sie müssen nicht daran denken, den Operator delete [] aufzurufen. – brianegge

+0

@brianegge: Was, wenn ich ein Array an eine externe C-Funktion übergeben muss, kann ich ihm einfach den Vektor geben? – dreamlax

+11

Sie können '& vector [0]' übergeben. – jamesdlin

Antwort

302

Es ist ein überraschend wenig bekannte Eigenschaft von C++ (wie durch die Tatsache belegt, dass niemand dies als eine Antwort noch gegeben hat), aber es hat tatsächlich eine spezielle Syntax für Default- einen Array initialisieren (na ja, technisch gesehen, ist es „Wert-initialize“ im Standard genannt):

new int[10](); 

Beachten Sie, dass die leeren Klammern verwenden müssen - Sie können nicht zum Beispiel verwenden (0) oder anderer Ausdruck (deshalb ist dies nur für Standardinitiativen nützlich lisation).

Dies wird durch ISO C++ 03 5.3.4 [expr.new]/15, die sagt ausdrücklich erlaubt:

Ein neuer-Ausdruck, der ein Objekt vom Typ T erzeugt, wie das Objekt initialisiert folgt :

...

  • Wenn der new-initializer die Form() hat, wird der Wert initialisiert (8.5);

und beschränkt nicht die Typen, für die dies erlaubt ist, während die (expression-list) Form ausdrücklich durch weitere Regeln im selben Abschnitt beschränkt ist, so dass es Typen erlaubt keine Array.

+1

Während ich zustimme, dass dies wenig bekannt ist, kann ich nicht (völlig) zustimmen, dass es wirklich sehr überraschend ist - es wurde in C++ 03 hinzugefügt, was die meisten Leute anscheinend fast ignoriert haben (da dies einer der wenigen neuen war) Dinge, die es hinzugefügt hat). –

+1

@Jerry: Ich muss zugeben, dass ich das noch nicht wusste (wahrscheinlich, weil ich, als ich den Standard gelesen hatte, schon C++ 03 war). Es ist bemerkenswert, dass alle Implementierungen, die ich kenne, dies unterstützen (ich denke, es ist so einfach zu implementieren). –

+1

Ja, es ist ziemlich trivial zu implementieren. Soweit es neu ist, war * all * "value initialization" neu in C++ 03. –

6

Ja, es gibt:

std::vector<int> vec(SIZE, 0); 

Verwenden Sie einen Vektor anstelle einer dynamisch zugewiesenen Arrays. Zu den Vorteilen gehört, dass Sie sich nicht die Mühe machen müssen, das Array explizit zu löschen (es wird gelöscht, wenn der Vektor den Gültigkeitsbereich verlässt), und auch, dass der Speicher automatisch gelöscht wird, selbst wenn eine Ausnahme ausgelöst wird.

Edit: Um weitere Drive-by downvotes von Leuten zu vermeiden, die nicht die Kommentare unten lesen, sollte ich klarstellen, dass diese Antwort nicht besagt, dass Vektor immer die richtige Antwort ist. Aber es ist sicher ein C++ - Weg, als "manuell" sicherzustellen, dass ein Array gelöscht wird.

Jetzt mit C++ 11 gibt es auch std :: array, das ein Array konstanter Größe modelliert (vs. Vektor, der wachsen kann). Es gibt auch std :: unique_ptr, das ein dynamisch zugewiesenes Array verwaltet (das mit Initialisierung kombiniert werden kann, wie in anderen Antworten auf diese Frage beantwortet). Irgendwelche davon sind ein C++ - Weg, als den Zeiger auf das Array IMHO manuell zu behandeln.

+10

Dies beantwortet nicht wirklich die Frage, die gestellt wurde. –

+1

Soll ich immer 'std :: vector' anstelle von dynamisch zugewiesenen Arrays verwenden? Was sind die Vorteile der Verwendung eines Arrays über einen Vektor und umgekehrt? – dreamlax

+0

Ich hätte in meiner Frage erwähnen sollen, dass das Array nach dem aktuellen Umfang bestehen bleiben muss. Muss ich 'new std :: vector' verwenden? – dreamlax

23

Unter der Annahme, dass Sie wirklich ein Array tun wollen und nicht ein std :: vector, die „C++ Weg“ wäre dies

#include <algorithm> 

int* array = new int[n]; // Assuming "n" is a pre-existing variable 

std::fill_n(array, n, 0); 

aber bewusst sein, dass unter der Haube dieser noch eigentlich nur eine Schleife, die weist jedem Element 0 zu (es gibt wirklich keine andere Möglichkeit, dies zu tun, abgesehen von einer speziellen Architektur mit Hardware-Level-Unterstützung).

+0

Es macht mir nichts aus, wenn die Schleife unter einer Funktion implementiert ist, ich wollte nur wissen, ob ich selbst eine solche Schleife implementieren musste oder nicht. Danke für den Tipp. – dreamlax

+3

Sie könnten überrascht sein. Ich war. In meiner STL (sowohl GCC als auch Dinkumware) wird std :: copy tatsächlich zu einem memcpy, wenn es erkennt, dass es mit eingebauten Typen aufgerufen wird. Ich wäre nicht überrascht, wenn std :: fill_n memset verwendet würde. –

+2

Nein. Verwenden Sie "Wert-Initialisierung", um alle Mitglieder auf 0 zu setzen. –

2

std::fill ist ein Weg. Nimmt zwei Iteratoren und einen Wert zum Füllen der Region mit. Das, oder die for-Schleife, wäre (nehme ich an) der C++ - Weg.

Zum Festlegen eines Arrays von primitiven Integer-Typen auf 0 ist memset in Ordnung, obwohl es möglicherweise Augenbrauen heben kann. Bedenken Sie auch calloc, obwohl es aufgrund der Besetzung etwas unpraktisch ist, aus C++ zu verwenden.

Ich für meinen Teil verwende ich immer eine Schleife.

(Ich mag keine Second-Vermutung Menschen Absichten, aber es ist wahr, dass std::vector ist, alle Dinge zu verwenden new[] gleich, bevorzugt ist.)

0

Typisch für dynamische Listen von Elementen, verwenden Sie ein std::vector.

Im Allgemeinen benutze ich memset oder eine Schleife für die dynamische Zuweisung des rohen Speichers, abhängig davon, wie die Variable diesen Bereich des Codes in der Zukunft erwartet.

7

Wenn der Speicher, den Sie zuweisen, eine Klasse mit einem Konstruktor ist, der etwas Nützliches tut, wird der Operator new diesen Konstruktor aufrufen und Ihr Objekt initialisiert lassen.

Wenn Sie jedoch eine POD oder etwas zuweisen, das keinen Konstruktor hat, der den Status des Objekts initialisiert, können Sie keinen Speicher zuweisen und diesen Speicher in einem Vorgang mit operator new initialisieren.Sie haben jedoch mehrere Optionen:

1) Verwenden Sie stattdessen eine Stapelvariable. Sie können in einem Schritt zuordnen und default-initialize, wie folgt aus:

int vals[100] = {0}; // first element is a matter of style 

2) verwenden memset(). Beachten Sie, dass, wenn das Objekt, das Sie zuweisen, kein POD ist, es eine schlechte Idee ist, es zu memstieren. Ein spezifisches Beispiel ist, wenn Sie eine Klasse mit virtuellen Funktionen mem- sieren, werden Sie die vtable wegblasen und Ihr Objekt in einem unbrauchbaren Zustand belassen.

3) Viele Betriebssysteme haben Aufrufe, die tun, was Sie wollen - auf einem Heap zuweisen und die Daten zu etwas initialisieren. Ein Windows-Beispiel wäre VirtualAlloc()

4) Dies ist normalerweise die beste Option. Vermeiden Sie es, die Erinnerung selbst zu verwalten. Sie können STL-Container zu tun, nur um etwas verwenden Sie mit rohen Speichern tun würden, einschließlich Aufteilung und alle in einer Initialisierung Schlagen:

std::vector<int> myInts(100, 0); // creates a vector of 100 ints, all set to zero 
1

Sie immer Memset verwenden:

int myArray[10]; 
memset(myArray, 0, 10 * sizeof(int)); 
+0

Ich verstehe, dass ich "memset" verwenden kann, aber ich war mir nicht sicher, ob dies der C++ - Weg war, um das Problem anzugehen. – dreamlax

+0

Yup, memset funktioniert in C++ genauso gut wie in C, um den linearen Speicher auf einen Wert zu setzen. –

+1

Es ist nicht wirklich der "C++ Weg", aber auch keine rohen Arrays. –

17

Es ist die Nummer ein Array von intrinsischen Typ von Verfahren zuzuordnen und alle diese Verfahren sind korrekt, obwohl welche man zu wählen, hängt ...

manuelle Initialisierung aller Elemente in der Schleife

int* p = new int[10]; 
for (int i = 0; i < 10; i++) 
{ 
    p[i] = 0; 
} 

Mit std::memset Funktion von <cstring>

int* p = new int[10]; 
std::memset(p, 0, 10); 

Verwendung std::fill_n Algorithmus aus <algorithm>

int* p = new int[10]; 
std::fill_n(p, 10, 0); 

Verwendung std::vector Behälter

std::vector<int> v(10); // elements zero'ed 

Wenn C++0x zur Verfügung, mit initializer list Funktionen

int a[] = { 1, 2, 3 }; // 3-element static size array 
vector<int> v = { 1, 2, 3 }; // 3-element array but vector is resizeable in runtime 
+0

sollte Vektor sein Wenn Sie p = new int [10]() hinzugefügt haben, hatten Sie eine vollständige Liste. – karsten

+0

@karsten Fest, Prost. – mloskot

Verwandte Themen