Registriert: Mo Jan 31, 2005 11:02 Beiträge: 432 Wohnort: Rheinlandpfalz
Hallo,
ich hab eine, vllt auch mehrere Fragen zu Pointern.
Angenommen ich habe so eine Klassen-Struktur:
Code:
Type
TChildClass =Class(TObject)
Private
...
Public
...
End;
PMainClass =^TMainClass;
TMainClass =Class(TObject)
Private
FChild : TChildClass;
FIrgendwas :Integer;
...
Public
...
End;
So, jetzt will ich, dass die ChildClass auf die MainClass zugreifen kann (auch auf den Private-Teil).
Dazu gibt es ja verschiedene Möglichkeiten:
1.
Ich speichere in der Private-Section der ChildClass einen Pointer vom Typ PMainClass auf die Hauptklasse (FParent),
welchen ich bei TChildClass.Create(hier) mitgebe. Dann kann ich immer, wenn ich Zugriff brauche,
mit
Code:
FParent.FIrgendwas:=12;
FParent^.FIrgendwas:=10;// geht auch, -> was ist der Unterschied??
2.
Ich übergebe beim Createn der ChildClass einen Pointer vom Typ Pointer auf die Hauptklasse, speicher diesen im Private-Teil der ChildClass ab (FParent) und den ich, wenn ich Zugriff auf die Hauptklasse aus einer Child-Methode brauche, wie folgt benutze:
Code:
Procedure TChildClass.DoSomething();
Var
ParentClass : TMainClass;
Begin
ParentClass := TMainClass(FParent);
ParentClass.FIrgendwas:=4;
End;
3.
Ich übergebe jedesmal, wenn ich in einer Methode der ChildClass Zugriff auf die MainClass brauche, einen Pointer vom Typ PMainClass mit:
Sicherlich gibt es noch andere (bessere?) Möglichkeiten.
Was mir jetzt aufgefallen ist:
- Nicht immer geht die 1. Möglichkeit. Manchmal bekomme ich Access-Violations. Warum?!
- Methode Nr.2 geht immer, ist aber etwas aufwändig.
- Methode Nr.3 geht auch immer, ist aber noch aufwändiger...
Weiß jemand warum meine Methode Nr.1 nicht immer geht? Ich würde es gerne verstehen.
Außerdem:
- Gibt es eine bessere Möglichkeit?
- Macht ihr es irgendwie anders?
- Braucht ihr sowas nicht?
Sind doch mehrere Fragen geworden Ich hoffe ihr könnt da etwas Klarheit rein bringen.
Registriert: Do Dez 05, 2002 10:35 Beiträge: 4234 Wohnort: Dortmund
Als erstes ein Mal. Klasseninstanzen sind bereits Pointer als vergiss bitte ganz schnell die Pointer auf Klassen! Das ist pfui bäh und führt eher nur zu Problemen. Es macht sicher in der ein oder anderen Situation Sinn oder ist dann notwendig. Allerdings versuche ich so etwas nach Möglichkeit immer zu vermeiden.
Um deine Definitonen anzupassen muss ja lediglich jede Klasse die andere kennen. Dazu gibt es Forward Deklarationen. Dann kannst du die ganze Zeit mit echten Typen arbeiten. Also sowohl im Konstruktor als auch überall anders. Das sieht dann in etwa so aus. Und dann kennt die TChildClass auch die TMainClass. Die Main kennt die Child ja sowieso, da sie nach ihr deklariert wurde.
Code:
type
TMainClass =class;// forward
TChildClass =class
private
fMain: TMainClass;
end;
TMainClass =class
private
fChild: TChildClass;
end;
Allerdings eine kleiner Ermahnung/Anregung. Wenn die Klassen sich gegenseitig benötigen deutet das meistens auf ungut durchdachtes Klassenkonzept hin. Denn Klassen sollten Modular sein und somit wären sie eher ziemlich stark miteinander verzahnt. Es passiert schon mal, dass man in solche Situationen kommt. Aber es deutet eher auf eine Schwachstelle des Konzeptes hin. Also sollte so etwas schon recht gut überlegt sein. Das aber nur als Anregung.
1. Der Unterschied zwischen beiden Methoden ist folgender: Keiner. Zu mindest derefenziert Delphi automatisch sobald es ein typisierter Pointer ist. Allerdings verlasse ich mich da nicht drauf. Ich dereferenziere grundsätzlich IMMER selber. Alleine auch schon dadurch, dass ich auf den ersten Blick sehe ob es ein Pointer ist oder nicht. Der ein oder andere lässt das lieber Delphi erledigen. Ist Geschmackssache. Aber mir persönlich gefällt das Automatische nicht.
Registriert: Di Mai 18, 2004 16:45 Beiträge: 2623 Wohnort: Berlin
Programmiersprache: Go, C/C++
Ich bevorzuge ebenfalls die Variante, die Lossy eX gepostet hat.
So kann man einfach beim erstellen oder über nen Property Parent die Elternklasse sauber festlegen.
_________________ "Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren" Benjamin Franklin
Registriert: Do Sep 02, 2004 19:42 Beiträge: 4158
Programmiersprache: FreePascal, C++
Dem Stimme ich zu. Mache ich bei meinen Listen-Klassen auch immer so:
Code:
type
TListItem =class;
TListClass =class;
TListItem =class(TObject)
constructor Create(AParent: TListClass);
private
FParent: TListClass;
public
property Parent: TListClass read FParent;
end;
TListClass =class(TObject)
public
function AddItem: TListItem;
end;
Gruß Lord Horazont
_________________ If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung. current projects: ManiacLab; aioxmpp zombofant network • my photostream „Writing code is like writing poetry“ - source unknown
„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb
Registriert: Mo Jan 31, 2005 11:02 Beiträge: 432 Wohnort: Rheinlandpfalz
Ich hat geschrieben:
Was mir jetzt aufgefallen ist - Nicht immer geht die 1. Möglichkeit. Manchmal bekomme ich Access-Violations. Warum?!
Wisst ihr warum das manchmal nicht geht? Das hab ich nämlich irgendwo drin, da funzt es, aber in anderen Situationen hat diese Technik mich völlig im Stich gelassen... Es regnete nur so AV's
Registriert: Do Dez 05, 2002 10:35 Beiträge: 4234 Wohnort: Dortmund
Na ja. Wollte auch gerade sagen, dass es immer klappen sollte. Aber wenn man sich mal genau betrachtet was da passiert wird es offentsichtlich. Denn typischerweise würde der Code folgendermaßen aussehen.
Code:
type
PMyClass =^TMyClass;
TMyClass =class(TClass);
procedure Add(Blah: PMyClass);
begin
FBlah: Blah;
end;
procedure Bloek;
var
Blah: TMyClass
begin
Blah := TMyClass.Create;
Add(@Blah);
// oder Addr(Blah) Ist das Gleiche.
end;
Das nur mal als Beispiel. Sollte ja vom Prinzip her gleich sein. Aber was passiert da jetzt? Es wird der Pointer der Variable Blah übergeben. Da Add sich den Pointer einer lokalen Variable merkt ist dieser Pointer nur so lange gültig wie die Variable gültig ist. Also nur so lange wie die Methode Bloek ausgeführt wird. Nach dem Ende der Methode ist diese Variable nicht mehr existent. Es kann dennoch passieren, dass der Inhalt an dieser Speicherstelle noch nicht überschrieben wurde. Das muss aber nicht. Spätestens das nächste mal wenn eine Methode etwas, an dieser Stelle, auf den Stack packt wurde sie überschrieben. Und dann bekommst du eine Zugriffsverletzung. Obwohl die Klasseninstanz weiterhin intakt ist. Aber der Speicher an dem der Pointer von ihr steht wurde überschrieben.
Bei direkter Benutzung wird die Adresse immer sauber kopiert weswegen so etwas nicht passiert. Um das auch zu rreichen hätte man also den Speicherbereich von Blah schon mit GetMem alloziieren müssen. Ansonsten ist es ein reine Glücksspiel und nur eine Frage wann es knallt. Ob es knallt steht dabei nicht mehr zur Debatte. Außer man weiß wirklich sehr genau wie lange der Pointer gültig ist. Aber beim Zwischenspeichern und später Benutzen wurde die Gültigkeit definitv überschritten.
Registriert: Do Dez 05, 2002 10:35 Beiträge: 4234 Wohnort: Dortmund
Öhm. Ich weiß jetzt nicht was protected mit verschiedenen Units zu tun hat? Eigentlich regelt protected ja "nur" die Zugriffsrechte. 2 Klassen die sich gegenseitig Verwendung und in unterschiedliche Units stecken resultieren grundsätzlich immer in einer zirkulären Referenz, da beide Klassen jeweils die Andere bereits im Interface benötigen. So etwas kann man nur lösen in dem man durch diverse Klassenumstellerreien etc. (die Liste an Feinheiten ist lang) die eine Unit aus dem Interface bekommt. Also in dem man verhindert, dass sie sich direkt benutzen.
ok ich habe ihn falsch verstanden, ich meine dass er auf die privateelemente der anderen klasse zugreifen will. Zirkulaere Abhaenigkeiten auf Klassenebene kann man so natuelich nicht loesen.
Zitat:
Allerdings eine kleiner Ermahnung/Anregung. Wenn die Klassen sich gegenseitig benötigen deutet das meistens auf ungut durchdachtes Klassenkonzept hin. Denn Klassen sollten Modular sein und somit wären sie eher ziemlich stark miteinander verzahnt. Es passiert schon mal, dass man in solche Situationen kommt. Aber es deutet eher auf eine Schwachstelle des Konzeptes hin. Also sollte so etwas schon recht gut überlegt sein. Das aber nur als Anregung.
Wieso sind parent-child Beziehungen (Ueblicherweise der Hauptgrund fuer solche abhaengigkeiten) schlechtes Design?
Registriert: Do Dez 05, 2002 10:35 Beiträge: 4234 Wohnort: Dortmund
Child-Parent Beziehungen. Ich habe bewusst nicht schlechtes Design geschrieben. Denn das ist es eigentlich nicht. Bzw liegt so etwas sehr stark im Auge des Betrachters. Und ich bin jemand der sehr sehr stark auf so etwas setzt. Zu mindest meistens. Ich meinte damit aber die extreme Verzahnung von unterschieldichen Klassen. Bei der Objektorientierten Programmierung geht es ja nicht nur darum Klassen zu benutzen. Sondern diese auch entsprechend modular zu gestalten.
Also sollten die Klassen bewusst so gestaltet werden, dass diese sich nur um ihre eigenen Sachen/Aufgaben kümmern. Wenn aber verschiedene Klassen Zugriff auf alles Mögliche haben, dann kann das darauf deutet, dass deren Aufgabenbereiche nicht klar geregelt ist. Außer deren Aufgabe ist die Verwaltung von Allem. Einer Bierflasche ist es ja schließlich auch egal wo sie sich im Kasten befindet und welche Farbe dieser hat. Aber deine Anmerkung ist auch berechtigt. Es kommt auch stark auf den einzelnen Fall an. Speziell bei baumartigen Strukturen benötigen die Kinder fast immer (sehr häufig) Informationen über das übergeordnete Element. Wobei das häufiger auch eher um die Verwaltung von Außen geht als darum, dass die Kinder auf die übergeordneten Elemente selber zugreifen müssen.
Man sollte sich nur generell gut überlegen ob das so auch Sinn macht. Denn der häufig aufgezählte Vorteil von OOP liegt ja in der Modularität und in der Wiederverwertbarkeit des Codes. Wenn die Klassen aber zu stark miteinander verbunden sind, dann lässt sich so etwas nur schwer durchsetzen. Das Ganze bezieht sich nicht nur auf die Anordnung der Klassen sondern auch welche Eigenschaften/Methoden sie zur Verfügung stellen/weiter reichen. Denn das beeinflusst maßgeblich die Struktur und das Zusammenwirken von Allem.
Mitglieder in diesem Forum: 0 Mitglieder und 6 Gäste
Du darfst keine neuen Themen in diesem Forum erstellen. Du darfst keine Antworten zu Themen in diesem Forum erstellen. Du darfst deine Beiträge in diesem Forum nicht ändern. Du darfst deine Beiträge in diesem Forum nicht löschen. Du darfst keine Dateianhänge in diesem Forum erstellen.