web-dev-qa-db-ger.com

Warum sollten C ++ - Programmierer die Verwendung von "new" minimieren?

Ich bin auf die Frage zum Stapelüberlauf gestoßen Speicherverlust mit std :: string bei Verwendung von std :: list <std :: string> und einer der Kommentare sagt Folgendes:

Verwenden Sie new nicht mehr so ​​oft. Ich kann keinen Grund erkennen, warum Sie irgendwo Neues verwendet haben. Sie können in C++ Objekte nach Wert erstellen. Dies ist einer der großen Vorteile der Verwendung der Sprache. Sie müssen nicht alles auf dem Heap zuordnen. Hör auf, wie ein Java Programmierer zu denken.

Ich bin mir nicht sicher, was er damit meint. Warum sollten Objekte in C++ so oft wie möglich nach Wert erstellt werden, und welchen Unterschied macht es intern? Habe ich die Antwort falsch interpretiert?

821
bitgarden

Es gibt zwei weit verbreitete Speicherzuweisungstechniken: automatische Zuweisung und dynamische Zuweisung. In der Regel gibt es für jeden Speicherbereich einen entsprechenden Bereich: den Stapel und den Heap.

Stapel

Der Stack ordnet den Speicher immer sequentiell zu. Dies ist möglich, da Sie den Speicher in umgekehrter Reihenfolge freigeben müssen (First-In, Last-Out: FILO). Dies ist die Speicherzuweisungstechnik für lokale Variablen in vielen Programmiersprachen. Es ist sehr, sehr schnell, weil es nur minimale Buchhaltung erfordert und die nächste zuzuweisende Adresse implizit ist.

In C++ wird dies als automatischer Speicher bezeichnet, da der Speicher am Ende des Gültigkeitsbereichs automatisch beansprucht wird. Sobald die Ausführung des aktuellen Codeblocks (mit {} abgegrenzt) abgeschlossen ist, wird automatisch der Speicher für alle Variablen in diesem Block erfasst. Dies ist auch der Moment, in dem Destruktoren aufgerufen werden, um Ressourcen zu bereinigen.

Haufen

Der Heap ermöglicht einen flexibleren Speicherzuweisungsmodus. Die Buchhaltung ist komplexer und die Zuordnung ist langsamer. Da es keinen impliziten Freigabepunkt gibt, müssen Sie den Speicher manuell freigeben, indem Sie delete oder delete[] (free in C) verwenden. Das Fehlen eines impliziten Freigabepunkts ist jedoch der Schlüssel zur Flexibilität des Heapspeichers.

Gründe für die Verwendung der dynamischen Zuordnung

Auch wenn die Verwendung des Heaps langsamer ist und möglicherweise zu Speicherverlusten oder Speicherfragmentierung führt, gibt es durchaus gute Anwendungsfälle für die dynamische Zuordnung, da diese weniger begrenzt ist.

Zwei Hauptgründe für die Verwendung der dynamischen Zuordnung:

  • Sie wissen nicht, wie viel Speicher Sie zum Zeitpunkt der Kompilierung benötigen. Wenn Sie beispielsweise eine Textdatei in eine Zeichenfolge einlesen, wissen Sie normalerweise nicht, welche Größe die Datei hat, und können daher nicht entscheiden, wie viel Speicher Sie zuweisen möchten, bis Sie das Programm ausführen.

  • Sie möchten Speicher zuweisen, der nach dem Verlassen des aktuellen Blocks bestehen bleibt. Beispielsweise möchten Sie möglicherweise eine Funktion string readfile(string path) schreiben, die den Inhalt einer Datei zurückgibt. Selbst wenn der Stapel den gesamten Dateiinhalt enthalten könnte, könnten Sie in diesem Fall nicht von einer Funktion zurückkehren und den zugewiesenen Speicherblock behalten.

Warum eine dynamische Zuordnung häufig nicht erforderlich ist

In C++ gibt es ein ordentliches Konstrukt namens Destruktor . Mit diesem Mechanismus können Sie Ressourcen verwalten, indem Sie die Lebensdauer der Ressource an der Lebensdauer einer Variablen ausrichten. Diese Technik heißt RAII und ist das Unterscheidungsmerkmal von C++. Es "verpackt" Ressourcen in Objekte. std::string ist ein perfektes Beispiel. Dieser Ausschnitt:

int main ( int argc, char* argv[] )
{
    std::string program(argv[0]);
}

reserviert tatsächlich eine variable Menge an Speicher. Das std::string -Objekt reserviert Speicher unter Verwendung des Heaps und gibt ihn in seinem Destruktor frei. In diesem Fall mussten Sie keine Ressourcen manuell verwalten und konnten dennoch die Vorteile der dynamischen Speicherzuweisung nutzen.

Insbesondere impliziert dies, dass in diesem Snippet:

int main ( int argc, char* argv[] )
{
    std::string * program = new std::string(argv[0]);  // Bad!
    delete program;
}

es gibt nicht benötigte dynamische Speicherzuordnung. Das Programm erfordert mehr Eingaben (!) Und birgt das Risiko, die Freigabe des Speichers zu vergessen. Dies geschieht ohne erkennbaren Nutzen.

Warum sollten Sie die automatische Speicherung so oft wie möglich verwenden?

Grundsätzlich fasst der letzte Absatz es zusammen. Wenn Sie die automatische Speicherung so oft wie möglich verwenden, werden Ihre Programme folgendermaßen ausgeführt:

  • schneller zu tippen;
  • schneller beim rennen;
  • weniger anfällig für Speicher-/Ressourcenlecks.

Bonuspunkte

In der genannten Frage gibt es zusätzliche Bedenken. Insbesondere die folgende Klasse:

class Line {
public:
    Line();
    ~Line();
    std::string* mString;
};

Line::Line() {
    mString = new std::string("foo_bar");
}

Line::~Line() {
    delete mString;
}

Ist tatsächlich viel riskanter als die folgende:

class Line {
public:
    Line();
    std::string mString;
};

Line::Line() {
    mString = "foo_bar";
    // note: there is a cleaner way to write this.
}

Der Grund dafür ist, dass std::string einen Kopierkonstruktor ordnungsgemäß definiert. Betrachten Sie das folgende Programm:

int main ()
{
    Line l1;
    Line l2 = l1;
}

Bei Verwendung der Originalversion stürzt dieses Programm wahrscheinlich ab, da es zweimal delete für dieselbe Zeichenfolge verwendet. Mit der geänderten Version besitzt jede Line -Instanz eine eigene String-Instanz mit jeweils eigenem Speicher, und beide werden am Ende freigegeben des Programms.

Weitere Hinweise

Die weitgehende Verwendung von RAII wird aus den oben genannten Gründen als Best Practice in C++ angesehen. Es gibt jedoch einen zusätzlichen Vorteil, der nicht sofort offensichtlich ist. Im Grunde ist es besser als die Summe seiner Teile. Der gesamte Mechanismus setzt sich zusammen . Es skaliert.

Wenn Sie die Klasse Line als Baustein verwenden:

 class Table
 {
      Line borders[4];
 };

Dann

 int main ()
 {
     Table table;
 }

reserviert vier std::string Instanzen, vier Line Instanzen, eine Table Instanz und den gesamten Inhalt des Strings und alles wird automatisch freigegeben .

973
André Caron

Weil der Stapel schneller und auslaufsicher ist

In C++ ist nur eine einzige Anweisung erforderlich, um Speicherplatz auf dem Stapel für jedes lokale Bereichsobjekt in einer bestimmten Funktion zuzuweisen, und es ist unmöglich, diesen Speicher zu verlieren. Dieser Kommentar wollte (oder hätte sagen sollen) wie "benutze den Stapel und nicht den Haufen".

166
DigitalRoss

Der Grund dafür ist kompliziert.

Erstens wird in C++ kein Müll gesammelt. Daher muss für jeden neuen ein entsprechender Löschvorgang durchgeführt werden. Wenn Sie diesen Löschvorgang nicht durchführen, liegt ein Speicherverlust vor. Nun, für einen einfachen Fall wie diesen:

std::string *someString = new std::string(...);
//Do stuff
delete someString;

Das ist ganz einfach. Aber was passiert, wenn "Do stuff" eine Ausnahme auslöst? Hoppla: Speicherverlust. Was passiert, wenn "Do stuff" return frühzeitig auftritt? Hoppla: Speicherverlust.

Und das ist für den einfachsten Fall. Wenn Sie diese Zeichenfolge an jemanden zurückgeben, muss dieser sie jetzt löschen. Und wenn sie es als Argument übergeben, muss die Person, die es empfängt, es löschen? Wann sollten sie es löschen?

Oder Sie können dies einfach tun:

std::string someString(...);
//Do stuff

Nein delete. Das Objekt wurde auf dem "Stapel" erstellt und wird zerstört, sobald es den Gültigkeitsbereich verlässt. Sie können das Objekt sogar zurückgeben und so seinen Inhalt an die aufrufende Funktion übertragen. Sie können das Objekt an Funktionen übergeben (normalerweise als Referenz oder Konstantenreferenz: void SomeFunc(std::string &iCanModifyThis, const std::string &iCantModifyThis). Und so weiter.

Alles ohne new und delete. Es ist keine Frage, wem das Gedächtnis gehört oder wer dafür verantwortlich ist, es zu löschen. Wenn Sie tun:

std::string someString(...);
std::string otherString;
otherString = someString;

Es versteht sich, dass otherString eine Kopie der Daten von someString hat. Es ist kein Zeiger; es ist ein separates Objekt. Möglicherweise haben sie denselben Inhalt, aber Sie können einen ändern, ohne den anderen zu beeinflussen:

someString += "More text.";
if(otherString == someString) { /*Will never get here */ }

Sehen Sie die Idee?

102
Nicol Bolas

Von new erstellte Objekte müssen möglicherweise delete sein, damit sie nicht auslaufen. Der Destruktor wird nicht aufgerufen, der Speicher wird nicht freigegeben, das ganze Stück. Da C++ keine Garbage Collection hat, ist dies ein Problem.

Durch Wert erzeugte Objekte (d. H. Auf dem Stapel) sterben automatisch, wenn sie den Gültigkeitsbereich verlassen. Der Destruktoraufruf wird vom Compiler eingefügt, und der Speicher wird bei Funktionsrückgabe automatisch freigegeben.

Intelligente Zeiger wie unique_ptr, shared_ptr lösen das schwebende Referenzproblem, erfordern jedoch Kodierungsdisziplin und weisen andere potenzielle Probleme auf (Kopierbarkeit, Referenzschleifen usw.).

In Szenarien mit vielen Multithreads ist new ein Konfliktpunkt zwischen Threads. Die Leistung kann beeinträchtigt werden, wenn new zu häufig verwendet wird. Die Erstellung von Stapelobjekten erfolgt per Definition thread-lokal, da jeder Thread einen eigenen Stapel hat.

Der Nachteil von Wertobjekten besteht darin, dass sie sterben, sobald die Host-Funktion zurückgegeben wird. Sie können dem Aufrufer keinen Verweis auf diese Objekte zurückgeben, sondern nur durch Kopieren, Zurückgeben oder Verschieben nach Wert.

73
Seva Alekseyev
  • C++ verwendet keinen eigenen Speichermanager. Andere Sprachen wie C #, Java haben einen Garbage Collector, der den Speicher verwaltet
  • C++ - Implementierungen verwenden normalerweise Betriebssystemroutinen, um den Speicher zuzuweisen, und zu viel Neues/Löschen kann den verfügbaren Speicher fragmentieren
  • Wenn der Speicher in einer Anwendung häufig verwendet wird, empfiehlt es sich, ihn vorab zuzuweisen und freizugeben, wenn er nicht benötigt wird.
  • Eine unsachgemäße Speicherverwaltung kann zu Speicherverlusten führen und ist sehr schwer zu verfolgen. Die Verwendung von Stapelobjekten im Funktionsumfang ist daher eine bewährte Technik
  • Der Nachteil bei der Verwendung von Stapelobjekten besteht darin, dass beim Zurückgeben, Übergeben an Funktionen usw. mehrere Kopien von Objekten erstellt werden. Intelligente Compiler kennen diese Situationen jedoch sehr gut und wurden hinsichtlich der Leistung optimiert
  • In C++ ist es sehr mühsam, wenn der Speicher an zwei verschiedenen Stellen zugewiesen und freigegeben wird. Die Verantwortung für die Freigabe ist immer eine Frage und meistens verlassen wir uns auf einige allgemein zugängliche Zeiger, Stapelobjekte (maximal möglich) und Techniken wie auto_ptr (RAII-Objekte).
  • Das Beste ist, dass Sie die Kontrolle über den Speicher haben, und das Schlimmste ist, dass Sie keine Kontrolle über den Speicher haben, wenn wir eine falsche Speicherverwaltung für die Anwendung verwenden. Die Abstürze, die durch Speicherbeschädigungen verursacht werden, sind die schlimmsten und schwer zu verfolgenden.
29
sarat

Ich sehe, dass ein paar wichtige Gründe, um so wenig neue wie möglich zu machen, übersehen werden:

Operator new hat eine nicht deterministische Ausführungszeit

Das Aufrufen von new kann dazu führen, dass das Betriebssystem Ihrem Prozess eine neue physische Seite zuweist. Dies kann recht langsam sein, wenn Sie dies häufig tun. Oder es ist bereits ein passender Speicherplatz verfügbar, den wir nicht kennen. Wenn Ihr Programm eine konsistente und vorhersehbare Ausführungszeit haben muss (wie in einem Echtzeitsystem oder einer Spiel-/Physiksimulation), müssen Sie new in Ihren zeitkritischen Schleifen vermeiden.

Operator new ist eine implizite Thread-Synchronisation

Ja, Sie haben mich gehört, Ihr Betriebssystem muss sicherstellen, dass Ihre Seitentabellen konsistent sind. Wenn Sie new aufrufen, erhält Ihr Thread eine implizite Mutex-Sperre. Wenn Sie konsequent new von vielen Threads aufrufen, serialisieren Sie Ihre Threads tatsächlich (ich habe dies mit 32 CPUs durchgeführt, von denen jede auf new trifft, um jeweils ein paar hundert Bytes zu erhalten, autsch! Royal Pita (zu debuggen)

Der Rest wie langsam, fragmentiert, fehleranfällig usw. wurde bereits in anderen Antworten erwähnt.

23
Emily L.

Pre-C++ 17:

Weil es leicht zu Undichtigkeiten kommt auch wenn Sie das Ergebnis in einen intelligenten Zeiger einwickeln.

Betrachten Sie einen "vorsichtigen" Benutzer, der sich daran erinnert, Objekte in intelligente Zeiger einzufügen:

foo(shared_ptr<T1>(new T1()), shared_ptr<T2>(new T2()));

Dieser Code ist gefährlich, weil es keine Garantie gibt, dass entweder shared_ptr konstruiert ist vor entweder T1 oder T2. Wenn also eines von new T1() oder new T2() fehlschlägt, nachdem das andere erfolgreich war, wird das erste Objekt gelöscht, da kein shared_ptr vorhanden ist, um es zu zerstören und die Zuordnung aufzuheben.

Lösung: Verwenden Sie make_shared.

Post-C++ 17:

Dies ist kein Problem mehr: In C++ 17 ist die Reihenfolge dieser Operationen eingeschränkt. In diesem Fall muss sichergestellt werden, dass nach jedem Aufruf von new() sofort der entsprechende Smart Pointer erstellt wird, ohne dass ein anderer erstellt wird Betrieb dazwischen. Dies impliziert, dass zum Zeitpunkt des zweiten Aufrufs von new() garantiert ist, dass das erste Objekt bereits in seinem intelligenten Zeiger eingeschlossen wurde, sodass im Falle eines Ausnahmefalls keine Lecks mehr auftreten.

Eine detailliertere Erklärung der neuen Auswertungsreihenfolge, die mit C++ 17 eingeführt wurde, lieferte Barry in einer anderen Antwort .

Vielen Dank an @ Remy Lebea für den Hinweis, dass dies immer noch ein Problem unter C++ 17 ist (obwohl weniger): Der Konstruktor shared_ptr kann seinen Steuerblock und Throw nicht zuordnen. In diesem Fall wird der übergebene Zeiger nicht gelöscht.

Lösung: Verwenden Sie make_shared.

18
Mehrdad

Zu einem großen Teil ist dies jemand, der seine eigenen Schwächen zu einer allgemeinen Regel erhebt. Es gibt nichts Falsches per se beim Erstellen von Objekten mit dem Operator new. Es gibt ein Argument dafür, dass Sie dies mit etwas Disziplin tun müssen: Wenn Sie ein Objekt erstellen, müssen Sie sicherstellen, dass es zerstört wird.

Der einfachste Weg, dies zu tun, besteht darin, das Objekt im automatischen Speicher zu erstellen, damit C++ es zerstören kann, wenn es den Gültigkeitsbereich verlässt:

 {
    File foo = File("foo.dat");

    // do things

 }

Beachten Sie nun, dass foo außerhalb des Bereichs liegt, wenn Sie nach der Endstrebe von diesem Block fallen. C++ ruft seinen dtor automatisch für Sie auf. Im Gegensatz zu Java müssen Sie nicht auf den GC warten, um ihn zu finden.

Hatten Sie geschrieben

 {
     File * foo = new File("foo.dat");

sie möchten es explizit mit übereinstimmen

     delete foo;
  }

oder noch besser, weisen Sie Ihren File * als "intelligenten Zeiger" zu. Wenn Sie nicht aufpassen, kann dies zu Undichtigkeiten führen.

Die Antwort selbst geht fälschlicherweise davon aus, dass Sie, wenn Sie new nicht verwenden, den Haufen nicht zuordnen. In C++ wissen Sie das nicht. Bestenfalls wissen Sie, dass auf dem Stapel mit Sicherheit eine kleine Menge an Speicher, beispielsweise ein Zeiger, zugeordnet ist. Überlegen Sie jedoch, ob die Implementierung von File in etwa so ist

  class File {
    private:
      FileImpl * fd;
    public:
      File(String fn){ fd = new FileImpl(fn);}

dann wird FileImplnoch auf dem Stapel zugewiesen.

Und ja, du solltest es besser haben

     ~File(){ delete fd ; }

auch in der Klasse; Andernfalls verlieren Sie Speicherplatz aus dem Heap, auch wenn Sie nicht anscheinend auf dem Heap allokieren.

17
Charlie Martin

new() sollte nicht als wenig verwendet werden. Es sollte so vorsichtig wie möglich verwendet werden. Und es sollte so oft verwendet werden, wie es der Pragmatismus vorschreibt.

Die Zuordnung von Objekten auf dem Stapel unter Berücksichtigung ihrer impliziten Zerstörung ist ein einfaches Modell. Wenn der erforderliche Bereich eines Objekts zu diesem Modell passt, muss new() nicht zusammen mit dem zugehörigen delete() verwendet und NULL-Zeiger überprüft werden. In dem Fall, in dem Sie viele kurzlebige Objekte auf dem Stapel haben, sollten die Probleme der Heap-Fragmentierung reduziert werden.

Wenn die Lebensdauer Ihres Objekts jedoch über den aktuellen Bereich hinausgehen muss, ist new() die richtige Antwort. Achten Sie nur darauf, wann und wie Sie delete() aufrufen und welche Möglichkeiten NULL-Zeiger bieten. Verwenden Sie dabei gelöschte Objekte und alle anderen Fallstricke, die mit der Verwendung von Zeigern einhergehen.

15

Wenn Sie new verwenden, werden Objekte dem Heap zugewiesen. Es wird im Allgemeinen verwendet, wenn Sie mit einer Erweiterung rechnen. Wenn Sie ein Objekt wie deklarieren,

Class var;

es wird auf den Stapel gelegt.

Sie müssen das Objekt, das Sie mit new auf den Haufen gelegt haben, immer mit destroy anrufen. Dies eröffnet die Möglichkeit von Speicherlecks. Objekte, die auf dem Stapel abgelegt werden, sind nicht anfällig für Speicherverluste!

13
Tim

Ein wichtiger Grund, um eine Überlastung des Heapspeichers zu vermeiden, ist die Leistung, insbesondere die Leistung des von C++ verwendeten Standard-Speicherverwaltungsmechanismus. Während die Zuweisung im trivialen Fall recht schnell sein kann, führt das Ausführen einer Menge von new und delete für Objekte mit ungleichmäßiger Größe ohne strenge Reihenfolge nicht nur zu einer Speicherfragmentierung, sondern kompliziert auch den Zuweisungsalgorithmus und kann in bestimmten Fällen die Leistung absolut beeinträchtigen.

Das ist das Problem, für das Speicherpools erstellt wurde, um die inhärenten Nachteile herkömmlicher Heap-Implementierungen zu mindern und den Heap dennoch nach Bedarf zu verwenden.

Besser noch, um das Problem insgesamt zu vermeiden. Wenn Sie es auf den Stapel legen können, dann tun Sie dies.

11
tylerl

Ich bin eher anderer Meinung als der Gedanke, "zu viel" Neues zu verwenden. Obwohl die ursprüngliche Verwendung von new durch Poster mit Systemklassen etwas lächerlich ist. (int *i; i = new int[9999];? Wirklich? int i[9999]; ist viel klarer.) Ich denke das ist, was die Ziege des Kommentators bekommen hat.

Wenn Sie mit Systemobjekten arbeiten, ist es sehr selten, dass Sie mehr als einen Verweis auf dasselbe Objekt benötigen. Solange der Wert der gleiche ist, ist das alles was zählt. Und Systemobjekte belegen normalerweise nicht viel Speicherplatz. (ein Byte pro Zeichen in einer Zeichenfolge). In diesem Fall sollten die Bibliotheken so konzipiert sein, dass sie die Speicherverwaltung berücksichtigen (sofern sie gut geschrieben sind). In diesen Fällen (bis auf eine oder zwei Nachrichten in seinem Code) ist new praktisch sinnlos und führt nur zu Verwirrung und potenziellen Fehlern.

Wenn Sie jedoch mit Ihren eigenen Klassen/Objekten arbeiten (z. B. der Line-Klasse des ursprünglichen Posters), müssen Sie sich selbst Gedanken über Probleme wie den Speicherbedarf, die Persistenz von Daten usw. machen. Zu diesem Zeitpunkt ist es von unschätzbarem Wert, mehrere Verweise auf denselben Wert zuzulassen - dies ermöglicht Konstruktionen wie verknüpfte Listen, Wörterbücher und Diagramme, bei denen mehrere Variablen nicht nur denselben Wert haben müssen, sondern auf genau dasselbe verweisen müssen Objekt im Speicher. Die Line-Klasse hat jedoch keine dieser Anforderungen. Der Code des ursprünglichen Posters benötigt also überhaupt kein new.

10
Chris Hayes

Ich denke, das Plakat sollte You do not have to allocate everything on theheap und nicht das stack lauten.

Grundsätzlich werden Objekte auf dem Stapel zugewiesen (sofern die Objektgröße dies zulässt), da die Stapelzuweisung kostengünstig ist und keine heapbasierte Zuweisung erforderlich ist, da der Zuweiser viel Arbeit leistet auf dem Heap zugewiesene Daten verwalten.

10
Khaled Nassar

Zwei Gründe:

  1. Das ist in diesem Fall nicht nötig. Sie machen Ihren Code unnötig komplizierter.
  2. Es weist Speicherplatz auf dem Heap zu und bedeutet, dass Sie daran denken müssen, ihn später delete zu speichern, da sonst ein Speicherverlust auftritt.
3
Dan

new ist das neue goto.

Erinnern Sie sich, warum goto so verunglimpft ist: Obwohl es sich um ein leistungsfähiges Tool für die Flusskontrolle auf niedriger Ebene handelt, wurde es häufig auf unnötig komplizierte Weise verwendet, was es schwierig machte, Code zu befolgen. Darüber hinaus wurden die nützlichsten und am einfachsten zu lesenden Muster in strukturierten Programmieranweisungen codiert (z. B. for oder while); Der ultimative Effekt ist, dass der Code, in dem goto der geeignete Weg ist, eher selten ist. Wenn Sie versucht sind, goto zu schreiben, tun Sie die Dinge wahrscheinlich schlecht (es sei denn, Sie wirklich wissen was du tust).

new ist ähnlich - es wird oft verwendet, um Dinge unnötig kompliziert und schwerer zu lesen, und die nützlichsten Verwendungsmuster können in verschiedene Klassen codiert werden. Wenn Sie neue Verwendungsmuster verwenden müssen, für die es noch keine Standardklassen gibt, können Sie Ihre eigenen Klassen schreiben, die sie codieren!

Ich würde sogar argumentieren, dass newschlimmer ist als goto, da new und delete Anweisungen gepaart werden müssen.

Wie bei goto tun Sie Dinge wahrscheinlich schlecht, wenn Sie jemals glauben, new verwenden zu müssen - insbesondere, wenn Sie dies außerhalb der Implementierung einer Klasse tun, deren Lebenszweck es ist, eine beliebige Dynamik zu kapseln Zuweisungen müssen Sie tun.

2
Hurkyl

Der Hauptgrund ist, dass Objekte auf dem Heap immer schwieriger zu verwenden und zu verwalten sind als einfache Werte. Das Schreiben von Code, der einfach zu lesen und zu warten ist, hat für jeden seriösen Programmierer immer die höchste Priorität.

Ein weiteres Szenario ist die von uns verwendete Bibliothek, die eine Wertesemantik bereitstellt und eine dynamische Zuordnung überflüssig macht. Std::string ist ein gutes Beispiel.

Für objektorientierten Code ist es jedoch ein Muss, einen Zeiger zu verwenden, dh, Sie müssen new verwenden, um ihn im Voraus zu erstellen. Um die Komplexität des Ressourcenmanagements zu vereinfachen, verfügen wir über Dutzende Tools, mit denen dies so einfach wie möglich gestaltet werden kann, z. B. intelligente Zeiger. Das objektbasierte Paradigma oder das generische Paradigma nimmt eine Wertesemantik an und erfordert weniger oder gar kein new, so wie es die Plakate an anderer Stelle angegeben haben.

Traditionelle Entwurfsmuster, insbesondere die in GoF genannten, verwenden new häufig, da es sich um typischen OO Code handelt.

1
bingfeng zhao

Noch ein Punkt zu all den obigen korrekten Antworten, es hängt davon ab, welche Art von Programmierung Sie durchführen. Beispiel: Kernel-Entwicklung unter Windows -> Der Stack ist stark eingeschränkt und Sie können möglicherweise keine Seitenfehler wie im Benutzermodus verarbeiten.

In solchen Umgebungen werden neue oder C-ähnliche API-Aufrufe bevorzugt und sogar benötigt.

Dies ist natürlich nur eine Ausnahme von der Regel.

1