2010-03-23 5 views
12

Sieht aus wie operator new und operator new[] haben genau die gleiche Signatur:Was ist der Zweck, einen separaten "Operator new []" zu haben?

void* operator new(size_t size); 
void* operator new[](size_t size); 

und genau das gleiche tun: entweder einen Zeiger auf einen groß genug Block von rohen Rück Speicher (nicht in irgendeiner Weise initialisiert) oder eine Ausnahme auslösen.

Auch wird operator new intern aufgerufen, wenn ich ein Objekt mit new und operator new[] schaffen - wenn ich ein Array von Objekten mit new[] erstellen. Dennoch werden die obigen zwei speziellen Funktionen von C++ intern genau auf die gleiche Weise aufgerufen, und ich sehe nicht, wie die beiden Aufrufe unterschiedliche Bedeutungen haben können.

Was ist der Zweck, zwei verschiedene Funktionen mit genau den gleichen Signaturen und genau dem gleichen Verhalten zu haben?

Antwort

6

in Design und Entwicklung von C++ (Abschnitt 10.3), erwähnt Stroustrup, dass, wenn der neuen Betreiber Für Objekt X selbst wurde verwendet, um ein Array von Objekt X zuzuweisen, dann würde der Schreiber von X :: operator new() auch mit der Array-Zuweisung umgehen müssen, was nicht die übliche Verwendung für new() ist und Komplexität hinzufügt. Daher wurde es nicht in Betracht gezogen, new() für die Array-Zuweisung zu verwenden. Dann gab es keine einfache Möglichkeit, verschiedene Speicherbereiche für dynamische Arrays zuzuordnen. Die Lösung bestand darin, separate Allokator- und Deallocator-Methoden für Arrays bereitzustellen: new [] und delete [].

+0

Dies ist die echte Antwort. Die anderen sind Spekulationen darüber, warum es so ist, basierend auf den Konsequenzen, jetzt, wo wir sie getrennt haben. –

7

Die Operatoren können überschrieben werden (für eine bestimmte Klasse oder in einem Namespace oder global). Dadurch können Sie separate Versionen bereitstellen, wenn Sie Objektzuordnungen anders als bei Arrayzuweisungen behandeln möchten. Beispielsweise möchten Sie möglicherweise aus verschiedenen Speicherpools reservieren.

7

Ich hatte einen ziemlich guten Blick darauf, und um es klar zu sagen, es gibt keinen Grund von einem Standpunkt der Schnittstelle.

Der einzig mögliche Grund, dass ich denken kann, ist eine Optimierung Hinweis für die Umsetzung zu ermöglichen, operator new[]ist wahrscheinlich aufgefordert werden, um größere Speicherblocks bereitzustellen; aber das ist eine wirklich, wirklich schwache Vermutung, wie Sie new eine sehr große Struktur oder new char[2], die nicht wirklich so groß zählen könnte.

Beachten Sie, dass operator new[] keinen magischen zusätzlichen Speicher für die Array-Anzahl oder irgendetwas hinzufügt. Es ist die Aufgabe des Operators new[], herauszufinden, wie viel Overhead (falls vorhanden) benötigt wird, und die korrekte Bytezahl an operator new[] zu übergeben.

[A Test mit gcc zeigt an, dass keine zusätzliche Lagerung durch new[] benötigt wird, wenn die Art des Arrayelementes einen nicht-trivialen desctructor hat gebaut.]

von einer Grenzfläche und Vertrag Standpunkt (außer das erfordern Verwendung der korrekten Entsprechungsfunktion) operator new und operator new[] sind identisch.

+0

Der Speicher wird nur benötigt, wenn der Operator delete [] dtors in einer Schleife aufrufen muss. (Was Sie fast gesagt haben, aber explizit für jeden, der dies liest und sich fragt.) –

+0

@Roger Pate: Ja, danke. Ich bin gut darin, Dinge "fast" zu sagen. Natürlich, wie es tatsächlich weiß, wie viele Destruktoren aufgerufen werden müssen, ist natürlich ein Implementierungsdetail, aber das Speichern mit dem dynamisch zugewiesenen Array ist die naheliegendste Implementierung. –

5

Ein Zweck ist, dass sie vom Benutzer getrennt definiert werden können. Also, wenn ich Speicher in einzelnen Heap-allokierte Objekte zu 0xFEFEFEFE und Speicher in Heap-allozierten Arrays zu 0xEFEFEFEF initialisieren will, weil ich denke, es wird mir beim Debuggen helfen, dann kann ich.

Ob das sich lohnt, ist eine andere Sache. Ich nehme an, wenn Ihr spezielles Programm meistens ziemlich kleine Objekte und ziemlich große Arrays verwendet, dann könnten Sie verschiedene Haufen verteilen, in der Hoffnung, dass dies die Fragmentierung reduzieren wird. Sie können aber auch die Klassen identifizieren, denen Sie große Arrays zuweisen, und operator new[] für diese Klassen einfach überschreiben. Oder operator new könnte basierend auf der Größe zwischen verschiedenen Haufen wechseln.

Es gibt tatsächlich einen Unterschied in der Formulierung der Anforderungen. Einer reserviert Speicher für jedes Objekt der angegebenen Größe, der andere reserviert Speicher für jedes Array der angegebenen Größe. Ich glaube nicht, dass es einen Unterschied gibt - ein Array der Größe 1 hat sicherlich die gleiche Ausrichtung wie ein Objekt - aber ich könnte mich irren.Die Tatsache, dass die Array-Version standardmäßig die gleiche zurückgibt wie die Objektversion, legt nahe, dass es keinen Unterschied gibt. Oder zumindest, dass die Ausrichtungsanforderungen für ein Objekt sind strenger als die auf einem Array, das ich keinen Sinn machen kann ...

+0

... vor allem als Objekt des Array-Typs ist immer noch ein Objekt. –

+0

Ja, ich dachte gerade an das Editieren, um das hinzuzufügen. Ein Array-Objekt ist jedoch kein * any * -Objekt, es ist Teil einer strikten Teilmenge aller Objekte und könnte daher möglicherweise schwächere Ausrichtungsanforderungen haben. Ich bin mir nicht sicher, ob es für 'sizeof (T [1])> sizeof (T)' legal ist, aber wenn es dann ist, kann man sich vorstellen, dass bei einigen Implementationen die Ausrichtungsanforderung für ein * Array * mit einer Größe von 8 Bytes nur möglich ist sei 4 (da dies das größte T sein kann), wohingegen die Ausrichtungsanforderung für ein * Objekt * mit einer Größe von 8 Bytes 8 sein könnte (weil es einen 8-ausgerichteten Typ gibt). –

+0

Ich bin ziemlich sicher, dass es nicht erlaubt ist, dass "sizeof (T [1])> sizeof (T)" aus dem Grund, dass ich mich nicht erinnern kann; etwas todo mit der Definition von Zeigerarithmetik vielleicht. Die Ausrichtung von 'T [1]' könnte jedoch> Größe von 'T [1]' sein. Ich denke, es ist legal für _placement-new_ und array in den Speicher, der von 'operator new' zugewiesen wird, genau weil ein Array ein Objekt ist. –

1

Norm sagt, dass new T Anrufe operator new() und new T[ ] zu einem Aufruf von operator new[](). Sie könnten sie überladen, wenn Sie möchten. Ich glaube, dass zwischen ihnen kein Unterschied besteht. Standard besagt, dass sie ersetzbar sind (3.7.3/2):

Die Bibliothek bietet Standarddefinitionen für die globalen Zuordnungs- und Freigabe-Funktionen. Einige globale Zuordnungs- und Freigabe-Funktionen sind ersetzbar (18.4.1). Ein C + + - Programm muss höchstens eine Definition einer ersetzbaren Zuweisungs- oder Aufteilungsfunktion enthalten. Eine solche Funktionsdefinition ersetzt die Standardversion in der Bibliothek (17.4.3.4). Die folgende Zuweisung und Freigabe-Funktionen (18.4) werden implizit in globalem Gültigkeitsbereich in jeder Übersetzungseinheit eines

void* operator new(std::size_t) throw(std::bad_alloc); 
void* operator new[](std::size_t) throw(std::bad_alloc); 
void operator delete(void*) throw(); 
void operator delete[](void*) throw(); 
Programm erklärt