Programmieren - alles kontrollieren 4.941 Themen, 20.708 Beiträge

Frage zu Vererbung und Zeiger auf Objekte

Yves3 / 2 Antworten / Baumansicht Nickles

Hallo

Folgender Code brachte mich dazu mal etwas genauer über Verebgung und Zeiger auf Basis- und abgeleitete Objekte nachzudenken:

spElemTemp = (MSXML2::IXMLDOMElementPtr) spNodeTemp;


Meine Interpretation, Gedanken und ein paar Fragen dazu, bitte korrigiert mich, wo ich falsch liege:
Hier wir scheinbar ein Zeiger einer Basisklasse einem Zeiger auf eine abgeleitete Klasse zugewiesen. Per Definition sollte das nicht gehen, deshalb braucht es hier auch einen Cast.
Umgekehrt ist das ja auch ohne Cast zulässig, wieso?
Nach purer "Bauernlogik" passt doch etwas kleines in etwas grosses, nicht aber etwas grosses in etwas kleines... dieser Gedanke scheint aber falsch zu sein.
Was ist ein Zeiger überhaupt genau?
Ein Zeiger zeigt auf einen Speicherbereich, hat aber auch einen Typ, demzufolge zeigt er von Punkt a bis Punkt b im Speicher (je nach Typgrösse). Richtig?
Wenn man das so sieht, würde aber die "Bauernlogik" zutreffen und beim Beispiel oben wäre kein Cast nötig... naütrlich mit dem unschönen Nebeneffekt, dass spElemTemp kein Vollwertiges Elementobjekt ist.
Ist es das hier überhaupt?

Viele Fragen :D. Wäre dankbar, wenn jemand etwas Licht ins Dunkle bringen könnte.
bei Antwort benachrichtigen
Andreas42 Yves3 „Frage zu Vererbung und Zeiger auf Objekte“
Optionen

Hi!

Die Bauernlogik ist eigentlich richtig, nur geht's hier nicht darum Töpfe zu stapeln, sondern Stecker zu verbinden. ;-)

Ich hab' das immer so gesehen: ein Zeiger hat Pins, die nur in seine Klasse passen (die hat die richtige Buchsenanzahl zum einstecken der Pins). Wird die Klasse abgeleitet, dann übernimmt sie das Buchsenlayout von der Elternklasse und man fügt ein paar neue Buchsen hinzu. Dazu gibt's dann Zeiger mit mehr Pins. Trotzdem passt aber auch ein Zeiger mit seinen Pins in die abgeleitete Klasse, weil die ja auch für ihn alle nötigen Buchsen mitbringt.

Man kann also, dass "Grosse" (die abgeleitete Klasse) auf den kleineren Zeiger stecken, dass passt schon. ;-)

Der Trick bei den Klassenableitungen ist ja gerade, dass man etwas anbaut bzw. hinzufügt und damit der (alte) Kern erhalten bleibt. Daher finden sich alle in der Elternklasse definierten Attribute und Methoden auch in der abgeleiteten Kindklasse wieder. Ein Zeiger der Elternklasse findet also alles vor, was auch in der Elternklasse vorhanden ist, daher passt er auch zu einer Instanz einer abgeleiteten Klasse. Die angebauten oder hinzugefügten Elemente kann er dann natürlich nicht wahrnehmen.

Bis dann
Andreas



Hier steht was ueber mein altes Hard- und Softwaregedoens.
bei Antwort benachrichtigen
Yves3 Andreas42 „Hi! Die Bauernlogik ist eigentlich richtig, nur geht s hier nicht darum Töpfe...“
Optionen

Vielen Dank!
Mir ist das jetzt schon etwas klarer.
Im Moment lese ich noch die entsprechenden Themen dieser Einführung hier durch: http://ladedu.com/cpp/kapitel11

EDIT1: Ich habe jetzt noch etwas herumexperimentiert und bin zum Schluss gekommen, dass folgendes beim Zugriff auf eine abgeleitete Klasse von einer Basisklasse aus möglich bzw. nicht möglich ist:


  • Zugreifen auf überladene Funktionen möglich

  • Zugreifen auf Funktionen mit gleichem Namen aber anderer Anzahl Parameter nicht möglich

  • Zugreifen auf Funktionen, die nur in der abgeleiteten Klasse existieren nicht möglich

  • Verwenden von Membervariablen, die nur in der abgeleiteten Klasse existieren in Funktionen der abgeleiteten Klasse möglich

  • Direktes Verwenden von neuen Membervariablen nicht möglich



EDIT2: Mir wurde das jetzt mal ganz ausführlich erklärt und ich habe jetzt den Durchblick :)

Für die, die s interessiert:

Hier ein Beispielprogramm, das die Problematik ziemlich gut aufzeigt.
Bei der zugelassenen Zuweisung von einem abgeleiteten Objekt an den Zeiger eins Basisobjekts warnt der Compiler, falls man versucht auf ein "neues" Objekt der abgeleiteten Klasse zuzugreifen. Bei dem Beispielprogramm geht es um das Gegenteil davon, wo der Compiler nicht mehr warnt:

#include "shape.h"

int main()
{
shape* Shape1 = new shape(2,3);
Shape1->Zeige();
circle* Circle1 = new circle(3.5f,7,8);
Circle1->Zeige();

shape *s1;
circle *c1;

char *test;

c1 = (circle *) Shape1;
test = (char*) Shape1;

printf("pointer = %ld\n"
"x = %ld\n"
"y = %ld\n"
"radius = %ld\n",
c1,&c1->x,&c1->y,&c1->radius );

printf("circle x = %d\n"
"ptr x = %d\n",
c1->y,
(int)*(test+sizeof(int)));

c1->x = 1;
c1->y = 2;
//c1->radius = 1.0; // Führt zu einem wüsten Crash


delete Shape1;
return 0;
}



Ausgabe:

Shape: X: 2 Y: 3
Shape: X: 7 Y: 8
Circle Radius: 3.500000
pointer = 3483228
x = 3483232
y = 3483236
radius = 3483240
circle y = 3
ptr y = 3

Eine kleine Anmerkung zu Ausgabe: Pointer und x sollten eigentlich die gleiche Adresse haben, das ist aber nur wegen dem Compilerinternem Zeugs nicht so. Trotzdem kann auf y mit einem Offset von 1*sizeof(x) zugegriffen werden.

Wichtig ist also, dass man hier genau weiss, was man macht.
Wie mir gesagt wurde handelt es sich bei dem ursprünglichen Codeausschnitt um ein Template, es ist also nicht so hässlich wie s aussieht :D
Ich werde mir Templates noch genauer anschauen.
bei Antwort benachrichtigen