AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Fragen zu OOP und Klassen

Ein Thema von Gonzo2 · begonnen am 22. Nov 2007 · letzter Beitrag vom 23. Nov 2007
Antwort Antwort
Seite 1 von 3  1 23      
Gonzo2

Registriert seit: 4. Nov 2007
42 Beiträge
 
#1

Fragen zu OOP und Klassen

  Alt 22. Nov 2007, 07:50
Ich will Klassen schreiben, nur verstehe ich kaum was davon. Ich hab mir einige Bücher, Tutorials und sonstige Onlinekurse durchgelesen, aber sie beantworten nicht alle meine Fragen. Es wird mir zu viel als gegeben angenommen oder wichtige Punkte mit einem Satz abgehandelt. Bei vielen Sachen will ich aber genauer wissen wieso es so funktioniert oder noch wichtiger, wieso es trotzdem funktioniert, obwohl ich es eigentlich anders machen sollte. Die ganzen Tutorials und Kurse werden anscheinend von Leuten geschrieben die die Materie beherrschen und sich deshalb bestimmte Fragen nicht mehr stellen. Deshalb behandeln sie sie auch nicht oder nicht so genau. So muß man bei Kursen einiges einfach akzeptieren ohne zumindest am Anfang zu verstehen wieso es so ist. Deshalb würde ich gerne hier einen Ergänzungskurs anfangen bei dem ich die Fragen stelle die für einen Anfänger interessant sind. Mir ist klar, daß es die Regel gibt pro Thread nur eine Frage zu stellen, aber ich hoffe man macht hier eine Ausnahme, so daß das ganze Thema zusammen bleiben kann. Also verschiedene Fragen aber zum Thema Klasse.

Ich hab viele Fragen, also fange ich mal an und nicht alles auf einmal.

Eine Klasse muß nicht kompliziert sein. Sie kann auch so aussehen:

Delphi-Quellcode:
TYPE
  TTest1 = class
  a: Byte;
end;
Das ist eine einfache Klasse und ich kann sie sofort nutzen ohne sie weiter auszubauen.

Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  Test1: TTest1;
begin
  Test1 := TTest1.Create;
  Test1.a := 200;
  ShowMessage(IntToStr(Test1.a));
  Test1.Free;
end;
Da bei Klassen geerbt wird, hat meine Klasse auch einiges von seinem Vorfahr geerbt, darunter auch die Methoden Create und Free. Da ich keine Klasse als Vorfahr genannt habe, wird TObject automatisch angenommen. Somit erbt meine Klasse alle Methoden von TObject. Baue ich auf einem anderen Vorfahrtyp, muß ich den angeben.

Machen wir es jetzt komplizierter und geben a einen Anfangswert.

Delphi-Quellcode:
TYPE
  TTest1 = class(TObject)
  a: Byte;
  constructor Create;
end;

constructor TTest1.Create;
begin
  a := 1;
end;
Immer noch eine einfache Klasse, aber hier bekommt a in Create einen Anfangswert. Das Ganze funktioniert übrigens ohne inherited. Liest man sich aber Bücher und Tutorials durch, so steht an erster Stelle, egal wie einfach eine Klasse ist und ob die sich von TObject ableitet, in Create immer inherited. Bei mir nicht. Hab ich jetzt ein Sakrileg begangen? Ich stelle an dieser Stelle mal die Prinzipfrage ob inherited immer nötig ist. Laut etlicher Bücher und Kurse ja, denn das macht man nun mal so. Genauer wird an dieser Stelle selten eingegangen. Create, wie übrigens auch Destroy, von TObject ist nebenbei gesagt leer und ohne Inhalt. Wozu also hier Create mit inherited aufrufen? Wie man merkt möchte ich hier etwas über inherited diskutieren, da überall nur steht, daß es genommen werden soll, ohne genauer drauf einzugehen.

inherited ist dazu da um den Vorfahr aufzurufen. Baue ich auf einer Klasse auf und will nicht nur den Namen erben, sondern auch die Eigenschaften die sich im Vorfahr hinter der Funktion verbergen, dann muß ich den Vorfahr mit inherited aufrufen.

Wie ich allerdings in vielen Beispielen sehen konnte wird inherited genommen ohne groß zu überlegen. Einen Fehler macht man anscheinend nicht wenn man inherited in eine Klasse einbaut die direkt von TObject abstammt, aber wozu nehmen wenn nicht nötig. Das gleiche gilt wohl auch für Destroy. Eine andere Frage ist wozu inherited bei Create immer vorne und bei Destroy immer hinten genommen wird? Rufe ich bei Create mit inherited den Vorfahr der Klasse auf, ist die Sache klar. Zumindest wenn ich etwas nutze was mir der Vorfahr liefert. Nutze ich nichts vom Vorfahr am Anfang, sondern ergänze den nur, ist es doch egal wo inherited steht.

Beispiele wie es nicht üblich ist:

Delphi-Quellcode:
constructor TTest2.Create;
begin
  StringList := TStringList.Create;
  inherited;
end;
Das gleiche gilt wohl auch beim Destroy.

Delphi-Quellcode:
constructor TTest2.Destroy;
begin
  inherited;
  StringList.Free;
end;
Ich zähle das Ganze nur wegen dem Verständnis auf. Man kann inherited in jedes Create schreiben, muß es aber nicht wenn es nicht notwendig ist. Wenn man es aber nicht weiß ob es notwendig ist, dann ist es nicht falsch es einzusetzen. Schaden tut es nicht. Bei der Reihenfolge muß inherited bei Create auch nicht immer an erster Stelle stehen, wenn es nicht notwendig ist. Wenn man es aber nicht weiß ob es notwendig ist, dann ist es nicht falsch Create an erster Stelle zu setzen. Schaden tut auch das in der Regel nicht. Hab ich das richtig verstanden?

Ansonsten ist mir nicht ganz klar wieso Delphi das nicht automatisch erledigt. Warum ruft der constructor nicht automatisch den Vorfahr auf? Oder gibt es Situationen wo man den Vorfahr mit Absicht nicht aufruft? Oder ist das mit der Reihenfolge so wichtig, daß man es den Programmierer überläßt. Oder hat das mit constructor und destructor selbst nichts zu tun, denn bei jeder Methode und Eigenschaft muß aufs Neue überlegt werden ob und wo man inherited einsetzt?

Dann folgt bei mir die Frage woher ich wissen soll wo ich inherited wann einsetzten soll? Bei Create und Destroy ist das mehr oder weniger bekannt und deshalb geregelt. Wie aber ist das bei anderen Eigenschaften und Methoden? Wie sieht es bei Assign aus? Woher soll ich wissen wo es hingehört? Bei TStrings.Assign ist inherited hinten, bei TStringList.Destroy ist es sogar mitten drin. Bedeutet das letztendlich, daß wenn ich eine Klasse schreibe ich mich erst mit dem Vorfahr auseinendersetzen muß? Wie gesagt, destroy von TStringList ist mitten drin.

Das waren meine ersten Verständnisfragen. Kann man also abschließend sagen, daß wenn man Klassen schreibt man sich immer erst mit der Vorgängerklasse auseinandersetzen muß? Und lediglich bei einfachen Klassen die auf TObject aufbauen man sich die Arbeit sparen kann, da in der Regel dann nur Create und Destroy angepaßt werden und bei denen macht es keinen Unterschied ob man inherited nimmt oder nicht oder vorne oder hinten?
  Mit Zitat antworten Zitat
Benutzerbild von Luckie
Luckie

Registriert seit: 29. Mai 2002
37.621 Beiträge
 
Delphi 2006 Professional
 
#2

Re: Fragen zu OOP und Klassen

  Alt 22. Nov 2007, 08:37
Du hast recht bei deinenen Beipsilen wäre es überflüssig. Man macht es aber meist trotzdem nur der Vollständigkeit halber. Bei CReate als erstes, weil man gegebenefalls Sachen im Konstruktor hat, die der Konstruktor der Vorfahrenklasse nicht wieder kaputt machen soll. Deswegen ruft Delphi den Konstruktor der Vorfahrenkalsse auch nicht automatisch auf, weil das nicht immer unbedingt gewünscht ist. Und bei Destroy als letztes, weil man erstmal seine abgeleitete Klasse aufräumt.

Guck dir mal mein Tutorial an: http//delphitutorials.michael-puff.de
Michael
Ein Teil meines Codes würde euch verunsichern.
  Mit Zitat antworten Zitat
Der_Unwissende

Registriert seit: 13. Dez 2003
Ort: Berlin
1.756 Beiträge
 
#3

Re: Fragen zu OOP und Klassen

  Alt 22. Nov 2007, 08:42
Hi, erstmal vorweg finde ich Deine Idee gut, da ich damals (als ich die OOP verstehen wollte) auch kein wirklich gutes Tut. gefunden hatte. Ist allerdings auch ein paar Jahre her, vielleicht sind die ja schon besser geworden. Trotzdem ist es immer gut zu fragen und natürlich werden die Fragen auch gerne beantwortet.

Zitat von Gonzo2:
Wie ich allerdings in vielen Beispielen sehen konnte wird inherited genommen ohne groß zu überlegen. Einen Fehler macht man anscheinend nicht wenn man inherited in eine Klasse einbaut die direkt von TObject abstammt, aber wozu nehmen wenn nicht nötig. Das gleiche gilt wohl auch für Destroy. Eine andere Frage ist wozu inherited bei Create immer vorne und bei Destroy immer hinten genommen wird?
Ganz so sinnlos wie es erscheint ist es nicht. Inherited ruft immer die gleichnamige Methode des Vorfahren auf. Dazu muss man kurz sagen, dass Klassen eben eine Art Tabelle besitzen, in der zu jeder virtuellen Methode ein Verweis existiert. Wird eine virtuelle Methode aufgerufen, so wird in der Tabelle nachgeschlagen, welche Methode verwendet werden soll. Anders als bei statischen Methoden können virtuelle auch durch einen Nachfahren überschrieben (direktive override) werden. Damit ändert man den Eintrag in dieser Tabelle, der verwendet wird. Ebenso kann man Methoden durch eine gleich benannte Methode überdecken. Hier wird in der Tabelle dann geschaut, von welcher "Art" / welchem "Typ" die Klasse ist. Das ist nötig, da jede erbende Klasse auch wie einer ihrer Vorfahren verwendet werden kann (z.B. kann eine TStringList als TStrings oder als TObject verwendet werden). Für jede der möglichen Klassen (also die eigentliche Klasse selbst, sowie alle Vorfahren) existiert dann ein Eintrag in dieser Tabelle für jede Methode.

Hierin liegt auch díe eigentliche Begründung für die Position von inherited. Das Create eines TObject hat die grundlegenden Dinge zu erledigen, es muss Speicher alloziert, diese Tabelle erzeugt und ein Verweis zurückgegeben werden. Vieles von dem Code bleibt aber verborgen, da es sehr spezifisch ist. Deshalb wirkt das Create nur leer, tatsächlich wird auch dort (natürlich) Code ausgeführt. Es macht natürlich für jede Klasse Sinn, dass man erst den nötigen Speicher alloziert usw. und erst dann weitermacht. Dass es auch ohne klappt hat nicht viel zu sagen, das kann auch von Fall zu Fall mal ordentlich nach hinten losgehen. Da gibt es dann eine Access Violation.
Ebenso ist das Destroy von TObject für das Aufräumen des Speichers zuständig. Dabei wird eben das eigentliche Objekt (also z.B. auch die genannte Tabelle) aus dem Speicher entfernt. Entsprechend kannst Du nicht mehr auf das Objekt zugreifen, wenn das inherited Destroy aufgerufen wurde. Die anderen Arbeiten die im (eigenen) Destruktor stattfinden sind i.d.R. das Aufräumen von Datentypen, auf die nur ein Verweis gespeichert wurde (dyn. Arrays, Klassen, Zeiger). Dort würde das inherited sonst nur den Speicher für den Verweis löschen, die eigentlichen "Objekte" (hier nicht im OO-Sinne gemeint) würden einfach unerreichbar im Speicher verbleiben.

So, muss leider gerade los, werde aber bei Gelegenheit mit noch den Rest anschauen und antworten.

Gruß Der Unwissende
  Mit Zitat antworten Zitat
Gonzo2

Registriert seit: 4. Nov 2007
42 Beiträge
 
#4

Re: Fragen zu OOP und Klassen

  Alt 22. Nov 2007, 10:22
Zitat von Luckie:
Guck dir mal mein Tutorial an: http//delphitutorials.michael-puff.de
Hab ich schon, ist ein Teil meines Grundwissens, aber es behandelt einige Punkte leider sehr kurz.

Zitat von Der_Unwissende:
Hierin liegt auch díe eigentliche Begründung für die Position von inherited. Das Create eines TObject hat die grundlegenden Dinge zu erledigen, es muss Speicher alloziert, diese Tabelle erzeugt und ein Verweis zurückgegeben werden. Vieles von dem Code bleibt aber verborgen, da es sehr spezifisch ist. Deshalb wirkt das Create nur leer, tatsächlich wird auch dort (natürlich) Code ausgeführt. Es macht natürlich für jede Klasse Sinn, dass man erst den nötigen Speicher alloziert usw. und erst dann weitermacht. Dass es auch ohne klappt hat nicht viel zu sagen, das kann auch von Fall zu Fall mal ordentlich nach hinten losgehen. Da gibt es dann eine Access Violation.
Ein interessanter Ansatz, der erstmal etwas nachdenklich macht. Soll man auch eine leere Create aufrufen, weil die Create von TObject den Speicher reserviert? Damit hast du mich im ersten Moment wirklich etwas verwirrt und nachdenklich gemacht. Aber dann habe ich mich gefragt, ist es nicht die Aufgabe von constructor den Speicher zu reservieren? Es stimmt schon, der Create von TObject reserviert Speicher, aber muß man ihn deswegen aufrufen? Ich weiß nicht inwieweit das für den Vorgänger wichtig ist. Muß man es machen oder erledigt mein constructor alles, auch für den Vorfahr? Somit wäre das Aufrufen des Vorfahrs nicht nötig.

Du hast mit den virtuellen und statischen Methoden etwas dem Thema vorweg gegriffen. Auf das Thema wollte ich etwas später eingehen. Auch da habe ich Fragen, aber alles der Reihe nach, sonst kommen wir durcheinander. Allerdings kann man insoweit schon darüber diskutieren ob Create ohne overload das Create des Vorgängers ersetzt oder überschreibt. Ich bin zwar noch nicht soweit, aber dann würde sich wirklich die Frage stellen ob man immer inherited aufrufen sollte.
  Mit Zitat antworten Zitat
Benutzerbild von Luckie
Luckie

Registriert seit: 29. Mai 2002
37.621 Beiträge
 
Delphi 2006 Professional
 
#5

Re: Fragen zu OOP und Klassen

  Alt 22. Nov 2007, 10:26
Zitat von Gonzo2:
Zitat von Luckie:
Guck dir mal mein Tutorial an: http//delphitutorials.michael-puff.de
Hab ich schon, ist ein Teil meines Grundwissens, aber es behandelt einige Punkte leider sehr kurz.
Welche? Das könnte man ja ändern.
Michael
Ein Teil meines Codes würde euch verunsichern.
  Mit Zitat antworten Zitat
xaromz

Registriert seit: 18. Mär 2005
1.682 Beiträge
 
Delphi 2006 Enterprise
 
#6

Re: Fragen zu OOP und Klassen

  Alt 22. Nov 2007, 10:29
Hallo,

Zitat von Der_Unwissende:
Hierin liegt auch díe eigentliche Begründung für die Position von inherited. Das Create eines TObject hat die grundlegenden Dinge zu erledigen, es muss Speicher alloziert, diese Tabelle erzeugt und ein Verweis zurückgegeben werden. Vieles von dem Code bleibt aber verborgen, da es sehr spezifisch ist. Deshalb wirkt das Create nur leer, tatsächlich wird auch dort (natürlich) Code ausgeführt.
Das stimmt nicht! Der Speicher (und die anderern genannten Dinge) wird (durch Compilermagic) beim Aufrufen des Konstruktors reserviert, und zwar bevor der Konstruktor ausgeführt wird. Durch inherited aufgerufene Konstruktoren und Destruktoren werden (wieder per Compilermagic) wie ganz normaler Methoden aufgerufen.

Der eigentliche Grund für das inherited auch bei TObject ist, dass Codegear ja jederzeit auf die Idee kommen können, Konstruktor oder Destruktor von TObject zu verwenden. Wenn dann in den Ableitungen das inherited fehlt...

Gruß
xaromz
I am a leaf on the wind - watch how I soar
  Mit Zitat antworten Zitat
Benutzerbild von sirius
sirius

Registriert seit: 3. Jan 2007
Ort: Dresden
3.443 Beiträge
 
Delphi 7 Enterprise
 
#7

Re: Fragen zu OOP und Klassen

  Alt 22. Nov 2007, 10:43
Schau dir mal das an. Da steht es sehr gut beschrieben.
Der "erste Aufruf" des Constructors führt also die Funktion ClassCreate aus, welche dem Objekt Leben einhaucht/ Speicher reserviert. Mit inherited rufst du nur den Vorfahr als ganz normale Methode auf. Wann und ob du das machst ist vollkommen dir überlassen. Und dabei ist es egal ob es sich um einen Constrcutor, einen Destructor oder eine Methode handelt.

Warum wird es nun standardmäßig so gehandhabt, wie du beschreibst?

a) Bei einer direkten Ableitung von TObject ist es derzeit nicht notwendig, da der Constrcutor von TObject ja leer ist. Aber wer sagt, dass es in Zukunft so bleiben wird. Falls es CG mal einfällt und in der Version 2008 etwas wichtiges im Constructor von TObject steht, dann brauchst du nicht deine ganzen constructors ändern. Ausserdem gibts seit 2006 Class Helper. Damit kannst du den Constructor in einem Projekt im Nachinein ändern.
Aber es bleibt trotzdem deine Entscheidung ob du Änderungen in TObject mitmachen willst.

b) Die Position des inherited erwies sich bisher so als am günstigsten. Wenn es irgendwie wichtig wist, die Reihenfolge zu beachten, dann meist in der beschriebenen Reihenfolge. Wenn es nicht wichtig war, dann kann man es auch so machen. Stört ja niemanden.
Dieser Beitrag ist für Jugendliche unter 18 Jahren nicht geeignet.
  Mit Zitat antworten Zitat
Gonzo2

Registriert seit: 4. Nov 2007
42 Beiträge
 
#8

Re: Fragen zu OOP und Klassen

  Alt 22. Nov 2007, 11:07
Zitat von xaromz:
Der eigentliche Grund für das inherited auch bei TObject ist, dass Codegear ja jederzeit auf die Idee kommen können, Konstruktor oder Destruktor von TObject zu verwenden. Wenn dann in den Ableitungen das inherited fehlt...
Das ist natürlich ein Argument, auch wenn ich nicht unbedingt glaube, daß es so kommen wird. Klassen verbessert man oder erweitert sie, aber man ändert nicht die Funktionsweise. Eine Änderung an dieser Stelle würde alle Klassen betreffen.
  Mit Zitat antworten Zitat
Tyrael Y.

Registriert seit: 28. Jul 2003
Ort: Stuttgart
1.093 Beiträge
 
Delphi 2007 Professional
 
#9

Re: Fragen zu OOP und Klassen

  Alt 22. Nov 2007, 15:13
Letzendlich, wie schon von dir erkannt und mehrfach gesagt, ist das inherited im Moment bei einer Klasse, die von TObject abgeleitet ist nicht notwendig.

Du sagst jetzt du kannst dir nicht vorstellen, daß man das Create von TObject verändern wird. Völlig ok deine Meinung. Es bleibt aber immer noch eine Meinung bzw. ein Glaube, es gibt keine Garantie dafür.

Man sollte stets defensiv programmieren und genau in diesem Moment ist das einfügen des inherited ein defensives Verhalten, was dich auf die sichere Seite bringt.


Mal ein kurzes anderes Beispiel zum defensiven Programmieren:


Delphi-Quellcode:
procedure TuWas();
var LMyObj: TEinTyp:
begin
  LMyObj := TEinTyp.Create;
  LMyObj.MachMalDeineAufgabe();
  LMyObj.Free;
end;

das ganze nochmal defensiv:
Delphi-Quellcode:
procedure TuWas();
var LMyObj: TEinTyp:
begin
  LMyObj := TEinTyp.Create;
  try
    LMyObj.MachMalDeineAufgabe();
  finally
    LMyObj.Free;
  end;
end;
Im ersten Fall könnte dein Objekt freigegeben werden, im zweiten Fall wird es garantiert freigegeben.
Levent Yildirim
Erzeugung von Icons aus Bildern:IconLev
  Mit Zitat antworten Zitat
Gonzo2

Registriert seit: 4. Nov 2007
42 Beiträge
 
#10

Re: Fragen zu OOP und Klassen

  Alt 23. Nov 2007, 09:21
Zitat von Tyrael Y.:
Im ersten Fall könnte dein Objekt freigegeben werden, im zweiten Fall wird es garantiert freigegeben.
Ja. Das ist kein unwichtiger Punkt, aber an dieser Stelle nicht so wichtig. Es ist schon klar, daß Objekte in einen Schutzblock gehören, aber bei Verständnisfragen ist alles was von dem Notwendigen ablenkt zuviel. Deshalb habe ich hier bei den Beispielen mit voller Absicht auf den Schutzblock verzichtet. Sonst werden bei mir Objekte eingepackt.
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 3  1 23      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 13:26 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz