Gondolf: Stack und Heap

Beitrag lesen

Das hab ich von OOP mit c++

Der gute Mann schreibt da IMHO einigen Unsinn.

„Kreis k; // statische Erzeugung
Statisch bedeutet hier, dass C++ das Objekt auf dem Stack speichert.“

Die Verwendung von "statisch" ist reichlich unklug, denn unter diesem Attribut versteht man bei OOP etwas gänzlich anderes als das eine Variable (und auch ein Objekt ist eine Variable) einfach auf dem Stack abgelegt wäre. Nichtsdestotrotz könnte man damit noch leben. Aber was dann folgt:

„Diese statische Erzeugung ist übrigens eine Spezialität von C++. Andere Sprachen, wie Pascal/Delphi, C#, Visual Basic(.NET) und Java kennen keine statisch erzeugten Objekte. In diesen Sprachen werden Objekte immer dynamisch erzeugt.“

Das ist in dieser Form schlicht und ergreifend – falsch. Das ist sowas von falsch, man sollte dem Mann das Schreiben von Lehrtexten zur Programmierung verbieten. Entweder schreibt man von Objekten, dann hat die Sprache Pascal (und Visual Basic?) da aber nichts zu suchen, denn die kennt keine Objekte, oder man bezieht sich auf Variablen allgemein, dann stimmt die Behauptung, der Stack wäre eine Spezialität von C++ aber nicht.

Jede deiner Variablen braucht logischerweise Platz im Speicher. Es gibt im Wesentlichen zwei Möglichkeiten, diesen Platz zu bekommen:

1. Der Stack, zu Deutsch Stapelspeicher. Der Stack ist ein zusammenhängender Speicherblock, den jedes Programm beim Start zugewiesen bekommt. Rufst du eine Funktion A auf, in der du beispielsweise ein int i deklariert hast, benutzt die Funktion A vier Bytes des Stapelspeichers. Ruft diese Funktion A eine weitere Funktion B auf, stapelt die zweite ihre Variablen auf die bereits vorhandenen Variablen von A. Und so geht das immer weiter, mit jedem Aufruf kommen weitere Variablen auf den Stapel, mit jedem Verlassen einer Funktion schrumpft der Stapel wieder.
Diese Variablen leben und sterben also mit dem Funktionsaufruf. Wohlgemerkt: Aufruf. Ruft A sich selbst auf, stapelt der zweite Aufruf von A seine Variablen auf jenen des ersten Aufrufs von A.

2. Der Hauptspeicher (der Autor nennt ihn Heap). Du kannst explizit Hauptspeicher vom Betriebssystem anfordern, meinetwegen wieder vier Bytes für dein int. Du bekommst dann vom System die Adresse des Speicherbereichs, kannst über diese Adresse auf die Bytes zugreifen und musst den Speicher wieder freigeben, sobald du ihn nicht mehr brauchst.
So eine Variable wäre von einem Funktionsaufruf unabhängig, du musst dich aber auch selbst darum kümmern, dass der nötige Speicher vor Variablennutzung reserviert und abschließend freigegeben wird.

Deine Objekte sind nichts weiter als Variablen mit ein wenig Zusatzfunktionalität. Der Speicher für ein Objekt kann bequem vom Stack genommen (QLabel label) oder aber vom Betriebssystem explizit reserviert werden (label = new QLabel).
In letzterem Fall beachte, dass du trotzdem noch die Variable label auf dem Stack hast: In ihr ist die Adresse des Objekts gespeichert.

Diese ganze Vorgehensweise ist _keine_ Spezialität von C++, eigentlich ist das nicht einmal eine Programmiersprachenangelegenheit, sondern obligt vielmehr dem Compiler oder Interpreter und fällt schon halb in den Bereich des Betriebssystems. Selbst bei einer prozessornahen Sprache wie Assembler, wo es keine Variablen im üblichen Sinne gibt, wird der Stack benutzt, und es gibt Konventionen der Prozessorhersteller, welches Prozessorregister die Adresse des Stacks enthalten soll.

Der Unterschied zwischen Stack und Hauptspeicher (Heap) liegt für dich als Programmierer einfach in deinem Bedarf. Der Stack enthält mehr oder weniger alle lokalen Variablen einer Funktion. Die explizite Reservierung von Speicher wird benutzt, wenn nicht bekannt ist, ob überhaupt, und wenn ja, wie viele Variablen (Daten) anfallen. Bei einem Texteditor kannst du beispielsweise nicht schon bei der Programmierung 100 Zeilen-Objekte vorab fest à la Zeile z[100] auf dem Stack anlegen; sinnvoll ist nur, diese Zeile für Zeile mit new Zeile() zu erzeugen, je nach Bedarf.