Welche Verwendungen gibt es für “Platzierung neu”?

Lesezeit: 11 Minuten

Welche Verwendungen gibt es fur Platzierung neu
Kopf Geek

Hat jemand hier jemals “placement new” von C++ verwendet? Wenn ja, wofür? Es sieht für mich so aus, als wäre es nur auf speicherabgebildeter Hardware nützlich.

  • Dies ist genau die Information, nach der ich gesucht habe, um Objektkonstruktoren für Boost-zugewiesene Speicherpools aufzurufen. (In der Hoffnung, dass diese Schlüsselwörter es jemandem in Zukunft leichter machen, ihn zu finden).

    – Sideshow-Bob

    14. September 2011 um 10:19 Uhr

  • Es wird in verwendet der C++11-Wikipedia-Artikel im Konstruktor einer Union.

    – Hallo Auf Wiedersehen

    5. November 2015 um 10:15 Uhr

  • @HalloAuf Wiedersehen, interessant! In dem Artikel, den Sie verlinkt haben, warum können Sie das nicht einfach tun? p = pt und verwenden Sie den Zuweisungsoperator von Point statt zu tun new(&p) Point(pt) ? Ich wundere mich über die Unterschiede zwischen den beiden. Würde erstere anrufen operator= on Point, während letzterer den Copy-Konstruktor von aufruft Point ? aber mir ist immer noch nicht ganz klar, warum das eine besser ist als das andere.

    – Andrei-Niculae Petre

    28. November 2018 um 20:29 Uhr

  • @Andrei-NiculaePetre Ich habe Placement new selbst nicht verwendet, aber ich denke, Sie sollten es zusammen mit dem Kopierkonstruktor verwenden, wenn Sie derzeit kein Objekt dieser Klasse haben, andernfalls sollten Sie den Kopierzuweisungsoperator verwenden. Es sei denn, die Klasse ist trivial; dann spielt es keine Rolle, welche davon Sie verwenden. Dasselbe gilt für die Zerstörung des Objekts. Wenn dies bei nicht-trivialen Klassen nicht richtig gehandhabt wird, kann dies sehr wahrscheinlich zu seltsamem Verhalten führen und möglicherweise sogar verursachen undefiniertes Verhalten in manchen Situationen.

    – Hallo Auf Wiedersehen

    28. November 2018 um 23:30 Uhr

  • @Andrei-NiculaePetre Eigentlich finde ich das Beispiel im Wikipedia-Artikel ziemlich schlecht, da nur davon ausgegangen wird, dass kein vorheriges Objekt existiert und dass sie eines konstruieren müssen. Dies ist nicht der Fall, wenn U::operator= wurde gerade angerufen.

    – Hallo Auf Wiedersehen

    28. November 2018 um 23:44 Uhr

1647288612 968 Welche Verwendungen gibt es fur Platzierung neu
Brian R. Bondy

Placement new ermöglicht es Ihnen, ein Objekt im Speicher zu konstruieren, der bereits allokiert ist.

Sie können dies zur Optimierung tun, wenn Sie mehrere Instanzen eines Objekts erstellen müssen und es schneller ist, den Speicher nicht jedes Mal neu zuzuweisen, wenn Sie eine neue Instanz benötigen. Stattdessen kann es effizienter sein, eine einzelne Zuweisung für einen Speicherblock durchzuführen, der mehrere Objekte enthalten kann, auch wenn Sie nicht alles auf einmal verwenden möchten.

DevX gibt a gutes Beispiel:

Standard-C++ unterstützt auch den Placement-New-Operator, der ein Objekt in einem vorab zugewiesenen Puffer erstellt. Dies ist nützlich beim Erstellen eines Speicherpools, eines Garbage Collectors oder einfach, wenn Leistung und Ausnahmesicherheit von größter Bedeutung sind (es besteht keine Gefahr eines Zuordnungsfehlers, da der Speicher bereits zugewiesen wurde und das Erstellen eines Objekts in einem vorab zugewiesenen Puffer weniger Zeit in Anspruch nimmt). :

char *buf  = new char[sizeof(string)]; // pre-allocated buffer
string *p = new (buf) string("hi");    // placement new
string *q = new string("hi");          // ordinary heap allocation

Möglicherweise möchten Sie auch sicher sein, dass an einem bestimmten Teil des kritischen Codes kein Zuordnungsfehler auftreten kann (z. B. in Code, der von einem Schrittmacher ausgeführt wird). In diesem Fall möchten Sie den Speicher früher zuweisen und dann die Platzierung neu innerhalb des kritischen Abschnitts verwenden.

Aufhebung der Platzierung neu

Sie sollten nicht jedes Objekt freigeben, das den Speicherpuffer verwendet. Stattdessen sollten Sie löschen[] nur der ursprüngliche Puffer. Sie müssten dann die Destruktoren Ihrer Klassen manuell aufrufen. Einen guten Vorschlag dazu finden Sie in den häufig gestellten Fragen von Stroustrup unter: Gibt es eine “Platzierungslöschung”?

  • Es ist nicht veraltet, da Sie diese Funktion benötigen, um Containerobjekte (wie Vektoren) effizient zu implementieren. Wenn Sie keinen eigenen Container erstellen, müssen Sie diese Funktion jedoch nicht verwenden.

    – Martin York

    21. Oktober 2008 um 21:19 Uhr

  • Es ist auch sehr wichtig, daran zu denken, einzubeziehen, da Sie sonst auf einigen Plattformen, die die Platzierung neu nicht automatisch erkennen, auf schreckliche Kopfschmerzen stoßen könnten

    – Ramon Zarazua B.

    24. September 2009 um 18:28 Uhr

  • Streng genommen ist es ein undefiniertes Verhalten, um anzurufen delete[] auf dem Original char Puffer. Platzierung verwenden new hat die Lebensdauer des Originals beendet char Objekte durch Wiederverwendung ihres Speichers. Wenn Sie jetzt anrufen delete[] buf Der dynamische Typ der Objekte, auf die gezeigt wird, stimmt nicht mehr mit ihrem statischen Typ überein, sodass Sie ein undefiniertes Verhalten haben. Es ist konsistenter zu verwenden operator new/operator delete um Rohspeicher zuzuweisen, der für die Verwendung durch Platzierung bestimmt ist new.

    – CB Bailey

    21. März 2010 um 15:10 Uhr

  • Ich würde auf jeden Fall auf die Verwendung des Haufens in einem Herzschrittmacher verzichten 🙂

    – Eli Bendersky

    9. Januar 2011 um 8:42 Uhr

  • @RamonZarazua Falscher Header, es ist #include <new>.

    – bit2shift

    26. April 2016 um 3:46 Uhr

1647288612 375 Welche Verwendungen gibt es fur Platzierung neu
Don Wakefield

Wir verwenden es mit benutzerdefinierten Speicherpools. Nur eine Skizze:

class Pool {
public:
    Pool() { /* implementation details irrelevant */ };
    virtual ~Pool() { /* ditto */ };

    virtual void *allocate(size_t);
    virtual void deallocate(void *);

    static Pool::misc_pool() { return misc_pool_p; /* global MiscPool for general use */ }
};

class ClusterPool : public Pool { /* ... */ };
class FastPool : public Pool { /* ... */ };
class MapPool : public Pool { /* ... */ };
class MiscPool : public Pool { /* ... */ };

// elsewhere...

void *pnew_new(size_t size)
{
   return Pool::misc_pool()->allocate(size);
}

void *pnew_new(size_t size, Pool *pool_p)
{
   if (!pool_p) {
      return Pool::misc_pool()->allocate(size);
   }
   else {
      return pool_p->allocate(size);
   }
}

void pnew_delete(void *p)
{
   Pool *hp = Pool::find_pool(p);
   // note: if p == 0, then Pool::find_pool(p) will return 0.
   if (hp) {
      hp->deallocate(p);
   }
}

// elsewhere...

class Obj {
public:
   // misc ctors, dtors, etc.

   // just a sampling of new/del operators
   void *operator new(size_t s)             { return pnew_new(s); }
   void *operator new(size_t s, Pool *hp)   { return pnew_new(s, hp); }
   void operator delete(void *dp)           { pnew_delete(dp); }
   void operator delete(void *dp, Pool*)    { pnew_delete(dp); }

   void *operator new[](size_t s)           { return pnew_new(s); }
   void *operator new[](size_t s, Pool* hp) { return pnew_new(s, hp); }
   void operator delete[](void *dp)         { pnew_delete(dp); }
   void operator delete[](void *dp, Pool*)  { pnew_delete(dp); }
};

// elsewhere...

ClusterPool *cp = new ClusterPool(arg1, arg2, ...);

Obj *new_obj = new (cp) Obj(arg_a, arg_b, ...);

Jetzt können Sie Objekte in einem einzigen Speicherbereich zusammenfassen, einen Zuordner auswählen, der sehr schnell ist, aber keine Freigabe vornimmt, Speicherzuordnung und jede andere Semantik verwenden, die Sie auferlegen möchten, indem Sie den Pool auswählen und ihn als Argument an die Platzierung eines Objekts übergeben neuer Betreiber.

  • Ja. Wir werden ziemlich schlau, aber es ist für diese Frage kein Thema.

    – Don Wakefield

    28. Januar 2011 um 20:28 Uhr

  • @jdkoftinoff haben Sie einen Link zu einem tatsächlichen Codebeispiel? scheint ziemlich interessant für mich!

    – Viktor

    14. Januar 2017 um 10:26 Uhr

  • @DonWakefield Wie gehen Sie mit der Ausrichtung in diesem Pool um? Sollten Sie nicht Ausrichtung als Argument an übergeben allocate() irgendwo?

    – Michail Wassiljew

    13. Juni 2018 um 17:16 Uhr

  • @MikhailVasilyev, in einer echten Implementierung würden Sie das natürlich erledigen. Nur Beispielcode.

    – Don Wakefield

    13. Juni 2018 um 17:36 Uhr

  • Was ist, wenn die Platzierung eine ungültige Adresse ist, sagen wir 0x0?

    – Charlie

    2. Oktober 2019 um 4:18 Uhr

Welche Verwendungen gibt es fur Platzierung neu
MSN

Es ist nützlich, wenn Sie die Zuordnung von der Initialisierung trennen möchten. STL verwendet Placement new, um Containerelemente zu erstellen.

Welche Verwendungen gibt es fur Platzierung neu
TED

Ich habe es in der Echtzeitprogrammierung verwendet. Wir normalerweise nicht Sie möchten nach dem Systemstart eine dynamische Zuweisung (oder Aufhebung der Zuweisung) durchführen, da es keine Garantie dafür gibt, wie lange dies dauern wird.

Was ich tun kann, ist, einen großen Teil des Speichers vorab zuzuweisen (groß genug, um jede Menge von dem zu speichern, was die Klasse benötigt). Sobald ich dann zur Laufzeit herausgefunden habe, wie die Dinge zu konstruieren sind, kann Placement New verwendet werden, um Objekte genau dort zu konstruieren, wo ich sie haben möchte. Eine Situation, von der ich weiß, dass ich sie verwendet habe, war, dabei zu helfen, eine Heterogenität zu schaffen kreisförmiger Puffer.

Es ist sicherlich nichts für schwache Nerven, aber deshalb machen sie die Syntax dafür etwas knorrig.

1647288614 300 Welche Verwendungen gibt es fur Platzierung neu
Ferruccio

Ich habe es verwendet, um Objekte zu konstruieren, die über alloca() auf dem Stapel zugewiesen wurden.

schamloser Stecker: Ich habe darüber gebloggt Hier.

  • interessanter Artikel, aber ich bin mir nicht sicher, ob ich den Vorteil verstehe, dies gegenüber zu verwenden boost::array. Kannst du das etwas erweitern?

    – GrahamS

    10. Februar 2011 um 11:52 Uhr

  • boost::array erfordert, dass die Größe des Arrays eine Kompilierzeitkonstante ist. Dies hat diese Einschränkung nicht.

    – Ferruccio

    10. Februar 2011 um 12:26 Uhr

  • @Ferruccio Das ist ziemlich cool, ich habe jedoch bemerkt, dass Ihr Makro etwas unsicher ist, nämlich Größe könnte ein Ausdruck sein. Wenn beispielsweise x+1 übergeben wird, würden Sie es zu sizeof(type) * x + 1 erweitern, was falsch wäre. Sie müssen Ihr Makro einklammern, um es sicherer zu machen.

    – Benj

    15. März 2012 um 11:22 Uhr

  • Die Verwendung mit alloca sieht für mich gefährlich aus, wenn eine Ausnahme ausgelöst wird, da Sie die Destruktoren für alle Ihre Objekte aufrufen müssen.

    – Goldesel

    12. April 2013 um 11:13 Uhr


1647288614 382 Welche Verwendungen gibt es fur Platzierung neu
dorsssc

Cheffreak: BINGO! Du hast es voll drauf – genau dafür ist es perfekt. In vielen eingebetteten Umgebungen zwingen externe Beschränkungen und/oder das allgemeine Verwendungsszenario den Programmierer dazu, die Zuordnung eines Objekts von seiner Initialisierung zu trennen. Zusammengefasst nennt C++ dies „Instanziierung“; aber wann immer die Aktion des Konstruktors explizit OHNE dynamische oder automatische Zuweisung aufgerufen werden muss, ist die Platzierung new der Weg, dies zu tun. Es ist auch der perfekte Weg, um ein globales C++-Objekt zu finden, das an die Adresse einer Hardwarekomponente gepinnt ist (Memory-mapped I/O), oder für jedes statische Objekt, das sich aus irgendeinem Grund an einer festen Adresse befinden muss.

  • interessanter Artikel, aber ich bin mir nicht sicher, ob ich den Vorteil verstehe, dies gegenüber zu verwenden boost::array. Kannst du das etwas erweitern?

    – GrahamS

    10. Februar 2011 um 11:52 Uhr

  • boost::array erfordert, dass die Größe des Arrays eine Kompilierzeitkonstante ist. Dies hat diese Einschränkung nicht.

    – Ferruccio

    10. Februar 2011 um 12:26 Uhr

  • @Ferruccio Das ist ziemlich cool, ich habe jedoch bemerkt, dass Ihr Makro etwas unsicher ist, nämlich Größe könnte ein Ausdruck sein. Wenn beispielsweise x+1 übergeben wird, würden Sie es zu sizeof(type) * x + 1 erweitern, was falsch wäre. Sie müssen Ihr Makro einklammern, um es sicherer zu machen.

    – Benj

    15. März 2012 um 11:22 Uhr

  • Die Verwendung mit alloca sieht für mich gefährlich aus, wenn eine Ausnahme ausgelöst wird, da Sie die Destruktoren für alle Ihre Objekte aufrufen müssen.

    – Goldesel

    12. April 2013 um 11:13 Uhr


Es ist eigentlich erforderlich, jede Art von Datenstruktur zu implementieren, die mehr Speicher zuweist, als für die Anzahl der eingefügten Elemente minimal erforderlich ist (dh alles andere als eine verknüpfte Struktur, die jeweils einen Knoten zuweist).

Nehmen Sie Behälter wie unordered_map, vectoroder deque. Diese weisen alle mehr Speicher zu, als für die bisher eingefügten Elemente minimal erforderlich ist, um zu vermeiden, dass für jede einzelne Einfügung eine Heap-Zuweisung erforderlich ist. Lassen Sie uns verwenden vector als einfachstes Beispiel.

Wenn Sie das tun:

vector<Foo> vec;

// Allocate memory for a thousand Foos:
vec.reserve(1000);

… das macht nicht wirklich tausend Foos. Es weist ihnen einfach Speicher zu/reserviert ihn. Wenn vector habe hier keine Platzierung neu verwendet, es wäre default-constructing Foos überall und müssen ihre Destruktoren sogar für Elemente aufrufen, die Sie gar nicht erst eingefügt haben.

Zuteilung != Aufbau, Befreiung != Zerstörung

Um viele Datenstrukturen wie die oben genannten zu implementieren, können Sie, allgemein gesagt, das Zuweisen von Speicher und das Erstellen von Elementen nicht als eine unteilbare Sache behandeln, und Sie können ebenso das Freigeben von Speicher und das Löschen von Elementen nicht als eine unteilbare Sache behandeln.

Es muss eine Trennung zwischen diesen Ideen geben, um zu vermeiden, dass Konstruktoren und Destruktoren unnötig links und rechts aufgerufen werden, und deshalb trennt die Standardbibliothek die Idee von std::allocator (das keine Elemente erstellt oder zerstört, wenn es Speicher zuweist/freigibt*) weg von den Containern, die es verwenden, die manuell Elemente mithilfe von „placement new“ erstellen und Elemente mithilfe expliziter Aufrufe von Destruktoren manuell zerstören.

  • Ich hasse das Design von std::allocator aber das ist ein anderes Thema, über das ich nicht schimpfen werde. 😀

Jedenfalls neige ich dazu, es häufig zu verwenden, da ich eine Reihe von standardkonformen C++-Containern für allgemeine Zwecke geschrieben habe, die nicht mit den vorhandenen Containern erstellt werden konnten. Darunter ist eine kleine Vektorimplementierung, die ich vor ein paar Jahrzehnten erstellt habe, um Heap-Zuweisungen in häufigen Fällen zu vermeiden, und ein speichereffizienter Trie (der nicht jeweils einen Knoten zuweist). In beiden Fällen konnte ich sie nicht wirklich mit den vorhandenen Containern implementieren, also musste ich verwenden placement new um unnötige Aufrufe von Konstruktoren und Destruktoren für Dinge zu vermeiden, die links und rechts unnötig sind.

Wenn Sie jemals mit benutzerdefinierten Zuweisungen arbeiten, um Objekte individuell zuzuweisen, wie z. B. eine freie Liste, möchten Sie dies natürlich auch verwenden placement newso (einfaches Beispiel, das sich nicht um Ausnahmesicherheit oder RAII kümmert):

Foo* foo = new(free_list.allocate()) Foo(...);
...
foo->~Foo();
free_list.free(foo);

1003010cookie-checkWelche Verwendungen gibt es für “Platzierung neu”?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy