Am 14. September 2017 haben wir eine überarbeitete Fassung unserer Datenschutzrichtlinie veröffentlicht. Wenn Sie video2brain.com weiterhin nutzen, erklären Sie sich mit diesem überarbeiteten Dokument einverstanden. Bitte lesen Sie es deshalb sorgfältig durch.

C++ Grundkurs

New-Operator

LinkedIn Learning kostenlos und unverbindlich testen!

Jetzt testen Alle Abonnements anzeigen
Die dynamische Allokation von zusätzlichem Speicher erfolgt in C++ mit Hilfe des new-Operators. Dieses Video erläutert die hierbei notwendige Vorgehensweise.
10:08

Transkript

In dieser Lektion möchte ich Ihnen die Speicherallokation in C++ vorstellen. Die Speicherallokation umfasst die Speicherallokation von einzelnen Objekten, von C-Arrays von Objekten, aber auch wie Sie mit Fehlern bei der Allokation von Speichern umgehen können. Es gibt aber auch noch den Operator placement "new". Der ist sehr interessant. Der erlaubt Ihnen, Objekte in einen definierten Speicherbereich anzulegen. Da muss ich gleich etwas vorwegschieben. Wenn Sie in C++ ein Objekt anlegen, geschehen zwei Dinge. Als erstes wird der Speicher besorgt, der rohe Speicher, der nackte Speicher. In diesen Speicher hinein wird das Objekt instanziiert. Da wird der Konstruktor aufgerufen. Also noch einmal zuerst wird der Speicher besorgt, und dann wird der Konstruktor aufgerufen. Den ersten Schritt, den Speicher besorgen, das können Sie in die Hand nehmen und durch eigene Routinen austauschen. Das macht das placement "new". Jetzt zeige ich erst mal den einfachen Anwendungsfall. Das ist das einfache "new". Durch "new" besorgen Sie sich einen Speicher für ein entsprechendes Objekt, in diesem Fall für ein "int", für ein "double" oder für einen "Point". Hier hinter dem Typ sehen Sie noch die Argumente vom Konstruktor. Hier wird der Konstruktor mit dem Wert 10.0 aufgerufen. Hier bei "Point" mit 1.0 und 2.0. Dementsprechend sind die Objekte mächtig initialisiert. Hier, da Sie kein explizites Argument aufrufen, wird der Default-Konstruktor ausgeführt. Was noch sehr interessant ist, wenn Sie ein eigenes Objekt haben, das Instanz einer abgeleiteten Klasse ist, werden mehrere Konstruktoren ausgeführt, also die Konstruktoren aller Basisklassen. Jetzt komme ich noch zum nächsten Anwendungsfall. new[] (mit eckigen Klammern) dient eben dazu, Speicher für C-Arrays zu allozieren, und die Syntax sehen Sie hier. Sie schreiben hinten einfach noch in eckigen Klammern die Anzahl der Objekte, die Sie allozieren wollen. In diesem Fall sind es 5 "double". In diesem Fall sind es 10 "Point". Hier gibt es etwas Besonderes, das man im Kopf behalten muss. Bei "double" und bei "Point" versucht der Compiler jetzt für jeden dieser Typen den Default-Konstruktor auszuführen. Der Default-Konstruktor ist der Konstruktor, der ohne Argumente auskommt. Wenn dieser Konstruktor für "Point" nicht existiert, für "double" existiert er natürlich. Wenn dieser Konstruktor für "Point" nicht existiert, schlägt dieser Aufruf fehl und Sie bekommen Kompilierungsfehler. Hier will ich explizit noch auf die STL-Container und den C++-String hinweisen. Die STL-Container "std::vector" und "std::array" sind dem Operator "new" extrem ähnlich. Das Schöne daran ist, Sie verwalten ihren Speicher automatisch. Das ist natürlich ein deutlicher Mehrwert. Das Gleiche trifft auch natürlich für den C++-String zu, der den Speicher automatisch verwaltet. Das heißt, Sie können in "std::vector" z. B. immer neue Elemente darauf schieben, und der alloziert neuen Speicher nach. Jetzt komme ich auf das bekannte placement "new". Placement "new" erlaubt es Ihnen, Objekte in einem definierten Speicherbereich anzulegen. Ich zeige Ihnen hier die Syntax. Hier besorge ich mir erst einen Speicher ein char-Array. Das Array ist groß genug, um ein Account Objekt in memory zu instanziieren. Das sehen Sie genau hier. Der große Unterschied zu den "new" Aufrufen von bevor ist, dass hier in Klammern noch "memory" steht. Das heißt, das Account Objekt "acc" wird in memory hinein instanziiert und nicht auf dem Heap angelegt, wie es der Compiler sonst automatisch macht. Damit haben Sie volle Kontrolle. Ich wiederhole es gerne noch einmal. Ein Objekt wird in C++ in zwei Schritten angelegt. Erst wird der Speicher besorgt und dann wird der Konstruktor in dem Speicher aufgerufen. Das Speicher besorgen übernehme jetzt ich mit dem placement "new". Damit Sie das machen können, benötigen Sie den Header <new, und jetzt gibt es noch eine Besonderheit. Placement "new" kann global und für eigenen Typen beladen werden. Ich stelle Ihnen hier typische Anwendungsfälle vor. Die explizite Speicherallokation. Das sehen Sie hier oben. Da sage ich einfach, ich will mein Objekt nicht auf dem Heap anlegen, sondern z. B. in einem statischen Speicherbereich. Vermeidung von Ausnahmen. Wenn die Speicherbesorgung fehlschlägt, wirft Ihnen die C++ Runtime eine Exception. Das heißt, das Programm stürzt ab, wenn Sie nicht mit der Exception umgehen. Sie können es aber auch so machen, dass Sie für den Fall, dass Sie den Speicher nicht erhalten, nur einen Nullpointer zurückbekommen. Das erlaubt Ihnen placement "new". Und zu was auch noch placement "new" sehr praktisch ist zu Debuggingzwecken. Wenn Sie placement "new" global überladen. Das heißt, es wird immer dann ausgeführt, wenn Sie Speicher sich mit "new" besorgen wollen, dann können Sie in diesen globalen placement "new" Operator eine Logik implementieren, die Ihnen immer mitzählt, wie viel Speicher Sie sich aktuell gerade besorgen. Jetzt gehe ich noch ein bisschen auf die fehlgeschlagene Allokation ein. Die Allokation kann fehlschlagen, weil Sie können z. B. sich einen Speicherbereich in einer Größe besorgen, den Ihr System nicht mehr anbietet. In diesem Fall bekommen Sie die Ausnahme vom Typ "std::bad_alloc". Um das zu vermeiden, können Sie die placement "new" anwenden. Das heißt, wenn Sie new oder new mit der Konstante "std::notrow" aufrufen, erhalten Sie keine "std::bad_alloc"-Ausnahme, sondern einfach nur einen Null-Zeiger zurück. Und das sehen Sie genau hier. Hier rufe ich "new" mit "std::nothrow" auf und das Ergebnis ist, dass ich bei fehlgeschlagener Allokation einen Null-Zeiger zurückerhalte. Diese Dinge will ich Ihnen jetzt natürlich in der Anwendung zeigen. Ich habe hier, ich wollte gerade sagen einfachen Wrapper, einen mitteleinfachen Wrapper um mein "int" geschrieben. Ist einfach ein Wrapper um int. Dieser Wrapper hat vier Methoden. Die gehe ich von oben nach unten durch. Erst mal verwende ich den Konstruktor hier, um die Variable "int" zu initialisieren. Dann sehen Sie es. Im ersten Konstruktor gebe ich aus "constructor for" und "this". "this" ist das aktuelle Objekt, das Sie aufrufen. In diesem Fall bekomme ich die Adresse des aktuellen Objekts raus. Das mache ich aus dem Grund, weil ich Ihnen zeigen will, wo das Objekt angelegt wird. Ich will Ihnen z. B. auch zeigen, wie placement "new" funktioniert. Im Konstruktor gebe ich this aus. Die Adresse ist von this. Im Destruktor tue ich das Gleiche. Jetzt kommen zwei Formen von placement new. In diesem Fall ist es keine besondere Form. In diesem Fall verwende ich den Operator "new", wie ihn der C++ Compiler verwenden würde, aber ich überlade ihn, um ein paar Informationen darzustellen. Als erstes gebe ich wieder einen String raus, damit wir wissen, dass er aufgerufen wird. Dann mache ich im Operator "new" das, was er tun soll. Der Operator "new" soll einen Zeiger auf neu allokierten Speicher zurückliefern vom Typ "void". Das ist seine Aufgabe. Diesmal mache ich das explizit, indem ich die Arbeit auf die C-Funktion "malloc" reduzieren. Im Prinzip programmiere ich hier das Default-Verhalten von Opertor "new" nach, eben um es zu visualisieren. Jetzt kommt das Interessante placement "new". Das erhält ein zusätzliches Argument "void* loc". Das ist ein Pointer auf "loc", auf einen Speicherbereich. Dieser Pointer wird eins zu eins weitergereicht. Hier geht er rein. Hier geht er raus. So simpel diese Methode ausschaut, diese Methode gibt mir extreme Mächtigkeit in die Hände. Diese Methode ermöglicht es mir, Instanzen vom Typ "MyInt" in speziellen Speicherbereichen anzulegen. Das war jetzt genug von "MyInt". Jetzt will ich es Ihnen in der Anwendung zeigen. Ich gehe zu den interessanten Punkten. Hier lege ich einen String auf die gewöhnliche Art und Weise auf den Heap an: new std::string ("on heap"). Pointer drauf. Nicht besonders spannend. Hier mache ich das Gleiche, aber hier verwende ich das erste Mal schon placement "new". Dann zeige ich Ihnen gleich die Ausgabe. Als erstes besorge ich mir einen Speicher von 100 Zeichen "new char [100]", halte mir den im Puffer, und jetzt zeige ich Ihnen die Adresse meiner zwei Objekte. Meines ersten Strings, den ich auf den Heap angelegt habe und meines zweiten Strings, den ich in "buf" angelegt habe, in den Puffer hier. Das der im Puffer angelegt ist, sehen Sie hier, weil ich hier explizit sage, "new buf". Damit sage ich, lege meinen neuen "std::string" in "buf" an, und "buf" steht für Buffer. Habe ich hier oben allokiert. Hier gebe ich mir die Adressen raus und Sie sehen, die beiden Objekte liegen sehr nahe beieinander. Das ist kein Wunder, weil ich die Allokation fast an identischen Punkten ausgeführt habe. Einmal hier implizit und einmal hier explizit. Da ich die Objekte dynamisch angelegt habe, muss ich sie wieder freigeben. Bei "buf" muss ich das Array von Zeichen wieder freigeben und on heap, da muss ich einfach nur den String freigeben. Das ist ein einzelnes Objekt. Jetzt gehe ich ein bisschen weiter im Text. Jetzt wird es relativ interessant. Jetzt wende ich placement "new" in einer anderen Variation noch einmal an. Hier lege ich "myIntHeap" an. "myIntHeap" ist eine Instanz vom Typ "MyInt", und das lege ich auf den Heap an. Das sehen Sie hier. In diesem Fall wird der normale Operator new aufgerufen, den ich hier oben definiert habe, und Sie sehen, hier wird er aufgerufen, und hier wird das Objekt wieder destruiert. Das Objekt "new MyInt" ruft den Konstruktor hier oben auf. Das ist das Default-Verhalten. Aber jetzt will ich diesen placement "new" Operator verwenden. Das geschieht dadurch, dass ich "MyInt(2014)" in "myIntBuf" anlege. "myIntBuf" muss man natürlich woher bekommen. Das habe ich hier oben im globalen Bereich angelegt. Da habe ich es angelegt. Hier instanziiere ich es rein, und hier kommt eine Besonderheit. Für das Objekt darf ich jetzt nicht den Default-Destruktor verwenden, weil der Default-Destruktor versuchen würde, das Objekt wieder im Heap freizugeben. Ich habe es aber nicht im Heap angelegt, sondern ich habe es im "myIntBuf" angelegt, darum muss ich den Destruktor explizit aufrufen. Jetzt noch der interessante Punkt. Sie sehen, wenn ich "myIntHeap" wie hier oben auf den Heap anlege, lade ich in diesem Speicherbereich. Wenn ich es hingegen mit placement "new" in einen ganz anderen Speicherbereich anlege, lande ich auch in einem ganz anderen Speicherbereich. In dieser Lektion habe ich Ihnen die Speicherverwaltung mit C++ vorgestellt. Ich habe Ihnen insbesondere vorgestellt, wie Sie sich mit "new" mit und ohne eckigen Klammern Speicher für Objekte und für Arrays von Objekten besorgen können. Ich habe Ihnen auch gezeigt, wie Sie mit einer fehlerhaften Speicherallokation umgehen können und wie Sie mit placement "new" Objekte in definierten Speicherbereichen anlegen können.

C++ Grundkurs

Steigen Sie in die mächtige Programmiersprache C++ ein und lernen Sie dabei alle wichtigen Funktionen mit Anwendungsbeispielen kennen.

8 Std. 14 min (147 Videos)
Derzeit sind keine Feedbacks vorhanden...
 

Dieser Online-Kurs ist als Download und als Streaming-Video verfügbar. Die gute Nachricht: Sie müssen sich nicht entscheiden - sobald Sie das Training erwerben, erhalten Sie Zugang zu beiden Optionen!

Der Download ermöglicht Ihnen die Offline-Nutzung des Trainings und bietet die Vorteile einer benutzerfreundlichen Abspielumgebung. Wenn Sie an verschiedenen Computern arbeiten, oder nicht den ganzen Kurs auf einmal herunterladen möchten, loggen Sie sich auf dieser Seite ein, um alle Videos des Trainings als Streaming-Video anzusehen.

Wir hoffen, dass Sie viel Freude und Erfolg mit diesem Video-Training haben werden. Falls Sie irgendwelche Fragen haben, zögern Sie nicht uns zu kontaktieren!