Wie sehen Klassen im "inneren" aus?
-
Hallo.
Ich habe mal eine Frage. OOP ist ja heute eigentlich nicht mehr wegzudenken, jedoch bin ich soweit informiert, dass die Maschinensprache ASM selbst gar kein OOP anbietet. Wie funktioniert das nun genau, wenn man in C++ eine Klasse erstellt? Wie wird die verwaltet, und woher weiß der Computer, welche Objekte zu welcher Instanz welcher Klasse gehören?Würde mich mal interessieren.
Gruß
-
Klassen sind nichts weiter als ein abstraktes Konzept.
Wenn du ein Klassenmethode aufrufst, dann wird z.B. der this-Zeiger als unsichtbarer Funktionsparameter mitgegeben. Der Zugriff auf ein Klassenelement ist nichts als Adressenrechnerei (Objektstartadresse + Elementoffset). Für Laufzeitpolymorphie hat man sich vtables ausgedacht.Wie wird die verwaltet, und woher weiß der Computer, welche Objekte zu welcher Instanz welcher Klasse gehören?
Wenn die Klasse über keine virtuelle Methoden und damit auch nicht über RTTI verfügt, dann ist zur Laufzeit nicht mehr feststellbar, welchen Typ ein Objekt an einer bestimmten Speicherstelle hat. Oder ob sich dort überhaupt ein Objekt befindet.
-
Objekte die keine virtuellen Methoden verwenden sind eigentlich sehr einfach zu verstehen. Die Membervariablen liegen einfach direkt hintereinander im Speicher. Und die Methoden sind im asm-code einfache Funktionen.
Was der Compiler also macht wenn er folgendes sieht
Class1 a; Class2 b; a.foo(b);
ist, dass er den Methodenaufruf durch einen Funktionsaufruf ersetzt
Class1foo(a,b);
und der Code dafür steht dann irgendwo in der Datei. Der Computer weiß also gar nichts von Objekten. Der Einzige der das weiß ist der Compiler und der übersetzt diese Methodenaufrufe immer durch Sprunganweisungen.
virtuelle Funktionen sind etwas komplizierter. Da erzeugt der Compiler eine neue Membervariable. Diese ist ein Zeiger auf eine Tabelle, die vtable. Da steht dann drin welche Funktionen das Objekt kennt. Da der Compiler ja genau weis wie die Objekte ausschauen, und wie er die Funktionen nummeriert, weiß er auch immer, welches Element er aus der Tabelle entnehmen muss, wenn er eine bestimmte Funktion aufrufen will. Dann könnte man sich vorstellen, dass er aus dem Funktionsaufruf oben sowas macht
//foo ist hier mal die 5. methode in der vtable a.vtable[5](a,b)
als nächstes löst man das a.vtable auf, das ist die Addresse von a(also wo das Objekt im Speicher beginnt) + ein Offset der beschreibt wann die membervariable beginnt
//Offset ist mal 3 *(&a+3)[5](a,b);
dann löst er noch den Arrayzugriff auf und landet dann irgendwie bei
funktionszeiger(a,b);
Und der Funktionszeiger ist dann das, was der Compiler als Sprunganweisung verwendet.
irgendwie so halt. Das wird halt bei den unterschiedlichen Vererbungstypen beliebig komplex und so gut wie jeder Compiler macht das auch etwas anders. Es gibt zum Beispiel Compiler die die vtable so verwenden, dass immer zuerst ein ganz normaler Funktionsaufruf gemacht wird und erst innerhalb dieser Funktion wird entschieden, welche Funktion wirklich aufgerufen werden soll.
-
Stichwort Application Binary Interface (ABI). Wie es genau aussieht hängt vom Compiler ab.