Wann new verwenden und wann nicht?



  • inc7 schrieb:

    wann weiß ich wann ich Möglichkeit 1 oder Möglichkeit 2 (s. Codebeispiel auf der ersten Seite) bevorzugen soll?

    Generell ist der Stack vorzuziehen. Grund: die Allokation auf dem Heap kostet Performance, das Speichermanagement muss außerdem selbst gehandelt werden (auch wenn Smartpointer das erleichtern), weil C++ keinen GC hat. Zugriffe über (smart-)Pointer auf Heap-Objekte dauern auch länger als auf Stackobjekte.
    ABER: ein Stack-Objekt hat nur Scope-Lebenszeit, soll heißen, wenn die Funktion oder der Block verlassen wird, wird das Objekt zerstört. Solltest du also Objekte erzeugen, die länger leben sollen als die aktuelle Funktion, kommst du am Heap nicht vorbei.



  • In C# können auch Wertetypen auf dem Heap sein, aber das ist ein Detail. Um die Lage in C++ beurteilen zu können, musst die Frage nach der Lifetime der Objekte stellen. Wenn du mit auto-Semantik leben kannst, dann solltest du sie verwenden. Wenn nicht, kannst du anfangen an new zu denken 😉



  • pumuckl schrieb:

    Zugriffe über (smart-)Pointer auf Heap-Objekte dauern auch länger als auf Stackobjekte.

    Hmmm... Ob man das so als generelle Aussage stehen lassen kann? Was bremst denn da? Cache misses? Wenn es da jetzt nichts anderes gibt, würde ich diese Aussage wieder zurück ziehen und getrennt auf den Cache hinweisen (und dass Cache misses teuer sind).



  • Ich würd sagen: In erster Linie die zusätzliche Indirektion 😉



  • dot schrieb:

    Ich würd sagen: In erster Linie die zusätzliche Indirektion 😉

    Kannst du da näher beschreiben und was genau meinst du mit Indirektion (ich kenne das Wort nicht)?



  • Auf ein Objekt am Heap greifst du über einen Zeiger zu. D.h. du musst zuerst den Wert des Zeigers auslesen um die Adresse für den eigentlichen Zugriff auf das Objekt zu bekommen.
    Auf ein Objekt am Stack kannst du dagegen direkt zugreifen.



  • dot schrieb:

    Auf ein Objekt am Stack kannst du dagegen direkt zugreifen.

    Wie soll das denn gehen? Der Stack liegt doch nicht in der CPU? Ok, wenn man sich erst den Pointer aus dem Stack holen muss um ihn zu dereferenzieren ist das eine Indirektion mehr, aber man kann ja schon öfter davon ausgehen, dass der Zeiger schon in einem Register liegt.



  • Eine x86 CPU hat ein eigenes Register für den Base Pointer auf den aktuellen Stackframe und unterstützt entsprechende Adressberechnungen direkt in Hardware...



  • cooky451 schrieb:

    Ok, wenn man sich erst den Pointer aus dem Stack holen muss um ihn zu dereferenzieren ist das eine Indirektion mehr, aber man kann ja schon öfter davon ausgehen, dass der Zeiger schon in einem Register liegt.

    Jepp. Aber erstens muss er da mal irgendwann reingeladen worden sein, und zweitens gibt es zumindest auf der x86-Architektur nicht so wahnsinnig viele allgemein verwendbare Register.



  • dot schrieb:

    Eine x86 CPU hat ein eigenes Register für den Base Pointer auf den aktuellen Stackframe und unterstützt entsprechende Adressberechnungen direkt in Hardware...

    Ja, aber es ist eben trotzdem eine Dereferenzierung. Die Frage ist (@bashar), ob der andere Zeiger auch schon in einem Register liegt. Ja, er muss mal reingeladen sein, aber wenn man ihn sehr oft hintereinander dereferenziert (-> das was Zeit braucht), liegt er genau so in einem Register. Insofern würde ich sagen, es gibt keine Optimierung die weniger erfolgsversprechend ist, als etwas vom Heap auf den Stack zu legen*. 🤡

    Das was am Stack halt so toll ist, ist, dass Speicheranforderungen nur eine Addition sind.



  • cooky451 schrieb:

    dot schrieb:

    Eine x86 CPU hat ein eigenes Register für den Base Pointer auf den aktuellen Stackframe und unterstützt entsprechende Adressberechnungen direkt in Hardware...

    Ja, aber es ist eben trotzdem eine Dereferenzierung.

    Und wenn du über einen Zeiger zugreifst hast du zwei Dereferenzierungen, außer der Wert des Zeigers liegt schon in einem Register. Und das wird eher nur der Fall sein, wenn du auf den Zeiger ständig (z.B. in einer kleinen Schleife) zugreifst. Ansonsten liegt der Zeiger bestenfalls im Cache...



  • Ich habe heute einige Objekte, die ich vorher mit Komposition benutzt habe, auf dem heap angelegt. So brauche ich in der Klasse nur die Vorwärtsdeklarationen, was zu weniger Abhängigkeiten beim kompilieren führt und die compilezeiten drückt.

    Qt hat durch das parent-Konzept auch das deleten für mich übernommen 🙂

    Es hat mich einfach genervt, dass ich nach einer kleinen Änderung in einem header mehr als 2 Minuten kompilieren muss..

    Natürlich geht das auch mit pimpl, aber das erhöht den implementierungsaufwand mehr und 1 mal new brauch man normalerweise trotzdem.


Anmelden zum Antworten