Unterschiede bei Instanzen
-
Ich versuche momentan zu verstehen was im einzelnen der Unterschied zwischen den beiden Instanzen einer klasse:
Hund hund;
und
Hund *hund=new Hund();
ist.
Wenn ich es richtig verstehe erzeuge ich bei Variante direkt 1 eine Instanz der Klasse,die Klassen Variablen werden per Standartkonstruktor initialisiert.Bei Variante 2 erzeuge ich,mit Hund *hund eine Referenz auf die mit new erzeugte neue Instanz und kann dem Konstruktor selbst parameter übergeben welche zur initialisierung genutzt werden können.
Ist das also vom Nutzen her der einzige Unterschied,dass ich bei Variante 1 keine Parameter an den Konstruktor übergeben kann,und dass ich bei Variante 2 dann halt selsbst Parameter übergeben kann,oder haben beide Varianten auch noch anderen Nutzen?
Und es wäre super,wenn mir jemand erklären könnte was genau überhaupt mit einer Instanz gemeint ist ich stelle mir es so vor,dass jede Instanz die der Programmierer von einer Klasse erzeugt praktisch die Klasse komplett in einem eigenen Speicherbereich abbildet,und sich halt nur die Werte selbst womit die einzelnen Instanzen arbeiten unterscheiden.Ist das soweit korrekt,oder stelle ich mir das ganz falsch vor?
-
Du kannst genauso gut bei #1 Parameter übergeben:
Hund hund("Wuffi");
Der unterschied ist, dass das erste einfach nur eine lokale Instanz ist, welche beim austritt aus dem aktuellen Block wieder destruktiert und irgendwann ggf. überschrieben wird und das untere ein Pointer auf einen Speicherbereich im Heap ist, welcher nicht selbstständig geleert wird, darum müsstest du dich dann selbst mit einem "delete" kümmern.
Und es wäre super,wenn mir jemand erklären könnte was genau überhaupt mit einer Instanz gemeint ist ich stelle mir es so vor,dass jede Instanz die der Programmierer von einer Klasse erzeugt praktisch die Klasse komplett in einem eigenen Speicherbereich abbildet,und sich halt nur die Werte selbst womit die einzelnen Instanzen arbeiten unterscheiden.Ist das soweit korrekt,oder stelle ich mir das ganz falsch vor?
Nicht "die ganze Klasse" wird komplett in einem Speicherbereich kopiert, die liegt weiterhin mit ihren Methoden im CODE-Teil des Programms, allerdings wird platz für die Variablen geschafft, die die Klasse beherbergt.
-
Ziemlich falsche Vorstellungen. Der Unterschied zwischen den beiden Varianten ist, dass
Hund hund;
die Speicherklasse "automatisch" hat. Das heißt, das Objekt wird in eben dem Gültigkeitsbereich (d.h. etwas zwischen geschweiften Klammern) erzeugt, wo du das obige definiert hast und wird automatisch(!) wieder zerstört, wenn der Gültigkeitsbereich verlassen wird (in der Regel das Ende einer Funktion). Du kannst auch ohne Probleme Parameter an den Konstruktor übergeben:
Hund hund("Waldi");
Im anderen Fall:
Hund *hund=new Hund();
Hier ist der Zeiger
hund
ebenfalls eine automatische Variable. Aber das, worauf er zeigt ist ein Objekt im Freispeicher, dass du dort mit new angelegt hast. Diese Objekte leben ewig, bis du sie mitdelete
manuell zerstörst. Das automatische Zerstören des Zeigers am Ende seines Gültigkeitsbereichs bedeutet nicht, dass ein delete auf das Objekt aufgerufen wird, auf das der Zeiger zeigt. Nur der Zeiger selbst wird zerstört. Das heißt, in diesem Fall ist es möglich, dass ein delete vergessen wird oder dass ein vorgesehenes delete eventuell übersprungen wird, weil der Programmierer nicht alle Eventualitäten bedacht hat (z.B. weil aufgrund einer Exception das delete übersprungen wird).Ist das schlimm? Ja! Denn wenn Objekte nicht sauber zerstört werden führt dies zu Ressourcenlecks.
Was kann man dagegen tun?
Zunächst einmal will man in der überwältigenden Zahl der Fälle, dass ein Objekt nur in einem überblickbaren Gültigkeitsbereich lebt, weil man es nur dort benötigt. Die von dir gezeigte erste Methode ist daher der Normalfall. Nur sehr selten benötigt man Feintuning für die Lebensdauer eines Objekts*. In dem Fall kann ein new ausnahmsweise mal angebracht sein. Aber wie stellt man sicher, dass auch das delete aufgerufen wird und auch nur ganz genau einmal? Hier kommen wieder die automatischen Variablen ins Spiel: Oben habe ich ja erklärt, dass das Zerstören eines einfachen Pointers kein delete auslöst. Aber was ist, wenn man keinen einfachen Pointer benutzt? Wenn man eine Klasse benutzt, die sich wie ein Pointer verhält, aber in ihrem Destruktor (also der Funktion, die aufgerufen wird, wenn ein Objekt der Klasse zerstört wird) ein delete aufruft? Dann löst sich diese Problematik ganz von alleine. Solche Klassen nennt man Smartpointer (oder Container, siehe Fußnote). Die Standardbibliothek bietet bereits eine Auswahl verschiedener Smartpointer für verschiedene Anwendungsszenarien, je nachdem, wann denn das delete nun genau passieren soll.In C++ wirst du so gut wie nie ein explizites new oder delete sehen und einfache Zeiger (keine Smartpointer), die auf ein mit new erzeugtes Objekt zeigen sind absolutes Tabu. Siehst du ein Lehrbuch oder einen Code, in dem das nicht so ist, dann kannst du zwei Dinge folgern:
-Das vorliegende Werk ist nicht so gut
-Der Autor kommt wahrscheinlich aus der Java-Ecke und ist nach C++ übergesiedelt, ohne die Eigenarten der Einheimischen zu erlernen. In Java ist new und Gepointere ganz normal und gut, aber das liegt da dran, dass die Sprache einfach anders funktioniert.*: Es gibt auch noch einen anderen, häufiger vorkommenden Grund, Objekte im Freispeicher zu erzeugen: Dann, wenn erst zur Laufzeit feststeht, ob und wie viele Objekte erzeugt werden müssen. Dynamische Arrays zum Beispiel. In dem Fall gilt aber das gleiche, was oben gesagt wurde. Außerdem benutzt man in dem Fall in aller Regel eine der Containerklassen aus der Standardbibliothek, die intern alles so machen, wie von mir beschrieben, so dass man das nicht jedes Mal selber programmieren muss.
--------------------------
Klasse vs. Objekt: Die Klasse ist eine "Idee", das Objekt die "Umsetzung". Um beim Hundebeispiel zu bleiben: Du hast sicher eine Vorstellung, eine Idee davon, was einen Hund ausmacht, in deinem Kopf. In deinem Kopf ist aber kein Hund, sondern nur eine Ansammlung von Eigenschaften, die du Hunden zuschreibst. Das ist die Klasse "Hund". Ein echter, lebender Hund ist eine Instanz dieser Klasse, ein Objekt.
Die Analogie passt aber nicht ganz, da du den echten Hund nicht anhand deiner Vorstellung erzeugt hast, sondern du deine Vorstellung danach erzeugt hast, dass du die Gemeinsamkeiten echter Hunde erkannt hast und dir danach die Vorstellung gebildet hast. In der Programmierung ist es andersherum, erst wird die Klasse gebildet und dann können anhand dieser Beschreibung konkrete Umsetzungen dieser Beschreibung gebildet werden, die Objekte. Eine passendere Analogie wäre daher vielleicht, dass du dir vorstellen kannst, wie wohl ein Bild eines Hundes aussehen würde und dann kannst du mehrere Instanzen dieser Vorstellung tatsächlich malen.
Eine technischere Ansicht:
class Foo { int bar; double d; int blah() { return bar; } };
Eine Instanz der Klasse Foo (nennen wir sie foo) wird im Speicher so aussehen:
Adresse von foo Ende von foo | | v v ------------------------------------------------------------------ ... Speicher | ein int | ein double | mehr Speicher ... ------------------------------------------------------------------ ^ ^ | | Adresse von foo.bar | | Adresse von foo.d
Ganz wichtig: Foo::blah ist kein Teil des Objekts! (Daher ist der Name auch Foo::blah und nicht foo.blah) Wenn die Funktion blah für das Objekt foo aufgerufen wird, so wird der Funktion ein versteckter Zeiger übergeben (der this-Zeiger), damit die Funktion weiß, auf welcher Instanz der Klasse sie arbeiten soll und wo das
bar
und dasd
dieser Klasse zu finden sind.
-
Die Analogie passt aber nicht ganz, da du den echten Hund nicht anhand deiner Vorstellung erzeugt hast, sondern du deine Vorstellung danach erzeugt hast, dass du die Gemeinsamkeiten echter Hunde erkannt hast und dir danach die Vorstellung gebildet hast.
Woher weißt du das?
-
Arcoth schrieb:
Die Analogie passt aber nicht ganz, da du den echten Hund nicht anhand deiner Vorstellung erzeugt hast, sondern du deine Vorstellung danach erzeugt hast, dass du die Gemeinsamkeiten echter Hunde erkannt hast und dir danach die Vorstellung gebildet hast.
Woher weißt du das?
Das lernt man, wenn man seine Zeit nicht mit Philosophie verschwendet
-
SeppJ schrieb:
Arcoth schrieb:
Die Analogie passt aber nicht ganz, da du den echten Hund nicht anhand deiner Vorstellung erzeugt hast, sondern du deine Vorstellung danach erzeugt hast, dass du die Gemeinsamkeiten echter Hunde erkannt hast und dir danach die Vorstellung gebildet hast.
Woher weißt du das?
Das lernt man, wenn man seine Zeit nicht mit Philosophie verschwendet
Scherzkekse!
SeppJ hats gelernt, indem er Jahre für Philosophie aufgebracht hat.