Discussion:
Frage zu abgeleiteten Klassen
(zu alt für eine Antwort)
M. Behrendt
2014-04-11 08:31:45 UTC
Permalink
Hallo!

Mal sehen, ob ich das jetzt verständlich erklären kann:

Ziel ist eine Art Fliessbandsimulation.

Ich habe eine Liste von Maschinen:

var maschinen_liste:Tmaschine;

welche wie folgt definiert sind:

type Tmaschine=class
maschine_nummer:word;
maschine_name:string;
maschine_type:byte;
anzahl_inputs:byte;
anzahl_outputs:byte;
inputliste:array of Tio_put;
outputliste:array of Tio_put;
public
function calc:boolean;
var bereit:boolean;
end;

Ich habe unterschiedliche Maschinentypen, z.B.
Schalter, type=0, anzahl_inputs:=0; anzahl_outputs:=1;
Anzeige, type=1; anzahl_inputs:=1; anzahl_outputs:=0;
Bohrmaschine, type=10, anzahl_inputs:=3; anzahl_outputs:=1;
Schleifmaschine, type=11, anzahl_inputs:=4; anzahl_outputs:=1;
usw.

dabei sind diese Maschinen von Tmasschine abgeleitet:
z.B. ganz trivial
type TSchalter=class(Tmaschine)
const maschine_type=0;
public
constructor create;
end;

oder auch
type Tbohrmaschine=class(Tmaschine)
const maschine_type=10;
public
function aktiviere(I_1,I_2:single):boolean;
constructor create;
end;

unter implementation
constructor TSchalter.create;
begin
anzahl_inputs:=0;
anzahl_outputs:=1;
end;

...

constructor Tbohrmaschine.create;
begin
anzahl_inputs:=2;
anzahl_outputs:=1;
end;

function Tmaschine.aktiviere(I_1,I_2:single):boolean;
begin
calc:=false;
...
if werkstück_fertig then calc:=true;
end;



Soweit.
Nun habe ich auf meiner Form einen Schalter zum aktivieren der ganzen Simulation, soll nur einen Arbeitsschritt machen, also keinen timer o.ä. aktivieren.

procedure TMainFrm.SpeedButton1Click(Sender: TObject);
begin
arbeitsschritt;
end;

mit
procedure arbeitsschritt;
var maschinen_durchzaehler:word;
begin
maschinen_durchzaehler:=0;
repeat
if maschinen_liste[maschinen_durchzaehler].maschinen_type=10 then
begin
maschinen_liste[maschinen_durchzaehler].bereit:=false;
maschinen_liste[maschinen_durchzaehler].aktiviere;
end;
inc(maschinen_durchzaehler);
until maschinen_durchzaehler=length(maschinen_liste);
end;

Meine Frage ist: Wenn ich in Arbeitsschritt die unter Tmaschine deklarierte function aktiviere; aufrufe, wie kann ich dann die Parameter der spezifischen Maschine mitübergeben, die ich z.B. bei Tbohrmaschine.aktiviere(i1,i2,i3,o1) brauche?
Bei bohrmaschine muss ich drei Inputs mitübergeben (Bohrertyp, Drehzahl, Lochtiefe), bei einer Anzeige nur einen Input (Anzeigewert, logisch).

Aber wie bekomme ich die unterschiedliche Zahl und Werte der Parameter in die allgemeinere function aktiviere; des Typs Tmaschine um ihn unter arbeitsschritt aufzurufen?
Selbst mit einem override über die Tmaschine.aktiviere krieg ich doch die Parameter nicht dorthin.

Ich hoffe, das war verständlich.
Weiss einer Rat?

Grüße,
Mike.
Carsten Thumulla
2014-04-11 11:40:33 UTC
Permalink
Post by M. Behrendt
Ziel ist eine Art Fliessbandsimulation.
Ich habe das nicht ganz verstanden.

Für eine Maschine macht man einen Typ, Klasse. Dann macht man eine
Instanz für jede Maschine, die da ist. Unterschiedliche Maschinen sind
unterschiedliche Typen, Klassen.

Nun braucht man noch Eigenschaften, die allen Maschinen gemeinsam sind,
wie Übernahme und Übergabe von Werkstücken, Freigabe Zuförderer nennt
man das oft. Dies baut man an die Typen dran oder besser, man kapselt
die Maschinen da hinein. Ein Zeiger zeigt auf die Maschineninstanz.

Das Leben bekommen die Maschinen vom Programmzeiger, Delphi. Dann
braucht man noch den Arbeitstakt oder den Takt für die Förderung. Der
wirkt dann auf den Teil, der bei allen Maschinen gleich ist und die
Förderung übernimmt.

In einer Liste hält man die Grundstruktur, die den Arbeitstakt bekommt
und die mit einem Zeiger auf die bestimmte Maschine zeigt. Bekommt die
Maschine den Takt, so arbeitet sie, so kann sie weiterfördern. Was sie
intern macht, das läuft in der Instanz, auf die der Zeiger zur Maschine
zeigt. Kleiner Trick, den Arbeitstakt, in der SPS deren Programmzeiger,
der Materialflußrichtung entgegenlaufen lassen, das spart eventuell
Merker und Hirnschmalz. Also die Taktstraße von hinten her abarbeiten
lassen. Das ist so, weil erst ein Platz frei sein muß, um auf diesen
fördern zu können. Sonst müßte der Zustand zwischengemerkt werden.


Carsten
--
http://thumulla.com/home/farbverlaeufe_berechnen.html
Hans-Peter Diettrich
2014-04-11 11:42:13 UTC
Permalink
Post by M. Behrendt
Ziel ist eine Art Fliessbandsimulation.
Aha ;-)
Post by M. Behrendt
var maschinen_liste:Tmaschine;
type Tmaschine=class
maschine_nummer:word;
maschine_name:string;
maschine_type:byte;
Diese Benennung riecht stark nach C/C++. In Delphi kann man sich
zumindest die "maschine_" Prefixes sparen, und nachher einfach mit
MeineMaschine.Name usw. zugreifen.
Post by M. Behrendt
anzahl_inputs:byte;
anzahl_outputs:byte;
Das ist jetzt Geschmackssache, aber in Delphi/Pascal wäre hier
AnzahlInputs gebräuchlicher (Mit Groß-/Kleinbuchstaben). Oder InputAnz,
damit man beim Eintippen schon nach wenigen Zeichen den Rest von der IDE
ergänzen lassen kann.
Post by M. Behrendt
type TSchalter=class(Tmaschine)
const maschine_type=0;
public
constructor create;
end;
Hmm, ob "const" hier so funktioniert kann ich nicht sagen, benutze die
neuen Features fast nie. Sonst entweder eine virtuelle Funktion, die den
Wert liefert, oder den Wert im Konstruktor in das Feld eintragen.
property Typ: byte read FTyp; //mit Schreibschutz
Post by M. Behrendt
Meine Frage ist: Wenn ich in Arbeitsschritt die unter Tmaschine deklarierte function aktiviere; aufrufe, wie kann ich dann die Parameter der spezifischen Maschine mitübergeben, die ich z.B. bei Tbohrmaschine.aktiviere(i1,i2,i3,o1) brauche?
Bei bohrmaschine muss ich drei Inputs mitübergeben (Bohrertyp, Drehzahl, Lochtiefe), bei einer Anzeige nur einen Input (Anzeigewert, logisch).
Dafür würde ich die Werte in Felder des Objekts eintragen, und dann erst
Aktiviere() aufrufen.
Post by M. Behrendt
Aber wie bekomme ich die unterschiedliche Zahl und Werte der Parameter in die allgemeinere function aktiviere; des Typs Tmaschine um ihn unter arbeitsschritt aufzurufen?
Selbst mit einem override über die Tmaschine.aktiviere krieg ich doch die Parameter nicht dorthin.
Nein, so geht das nicht. Höchstens über einen Array-Parameter (C:
"..."), wenn der bei virtuellen Methoden zulässig ist.

Ansonsten für jeden Parameter-Satz eine eigene (overload) virtuelle
Funktion, von denen die zur Maschine passende in der abgeleiteten Klasse
überschrieben wird. Dann kann es aber passieren, daß man eine falsche
(nicht-überschriebene) Funktion aufruft und sich wundert, warum nichts
passiert. Deshalb sollte in der Basis-Implementierung eine Fehlermeldung
ausgegeben werden, für den Aufruf einer nicht überschriebenen Methode.

Etwas sicherer ginge das mit einem eigenen Parameter-Record für jede
Aktion, dann wäre beim Aufruf MeineMachine.Aktiviere(Bohren) erkennbar,
was gemeint ist, und im Record Bohren:TBohrParameter stehen die
benötigten Werte drin.

Oder einen einzigen (großen) Parameter-Record, der auch noch einen
Aktionstyp enthält. Dafür könnte man dann (platzsparender) eine
virtuelle Basisklasse schreiben, die nur die Aktion (Bohren,
Schleifen...) enthält, und die zu jeder Aktion gehörenden Parameter
werden in die abgeleiteten Klassen eingebaut. Dann kann jede Maschine
sogar noch prüfen, ob sie die übergebene Aktion (Objekt) auch ausführen
kann. Oder man erzeugt eine Liste der nacheinander auszuführenden
Aktionen, und sucht dann in einer Schleife die jeweils dazu passende
Maschine - am Fließband sollte das immer die nächste Maschine
(Arbeitsstation) sein. Oder die aktuelle Maschine führt alle Aktionen in
der Liste aus, die sie kann, bevor das Werkstück zur nächsten Maschine
weitergegeben wird...

DoDi
Jakob Achterndiek
2014-04-11 12:30:03 UTC
Permalink
[..] wie bekomme ich die unterschiedliche Zahl und Werte der
Parameter in die allgemeinere function aktiviere; des Typs
Tmaschine um ihn unter arbeitsschritt aufzurufen?
Selbst mit einem override über die Tmaschine.aktiviere krieg
ich doch die Parameter nicht dorthin.
Ich hoffe, das war verständlich.
Weiss einer Rat?
Mir fällt folgende Möglichkeit ein. Die ist nicht elegant,
aber bei mir an anderer Stelle erprobt:

1. Die Funktionsaufrufe aller Maschinen bekommen die gleiche
Form mit jeweils denselben Variablen als Parameter.
2. Die Aufrufe der Funktionen liefern die benötigten Parameter
mit. Wenn das weniger als die maximal mögliche Anzahl sind,
dann werden Dummies übergeben.

Beispiele:

Alle Maschinen erhaltn denselben Funktionsaufruf
function aktivieren(an_aus: boolean;
einName: string;
Zahl_1, Zahl_2, Zahl_3: single): boolean;

Der Ein-Aus-Schalter wird aufgerufen mit
with Schalter do begin
an_aus := true; // wahlweise := false;
leer := '';
dummy := 0;
Ergebnis := aktivieren(an_aus, leer, dummy, dummy, dummy);
end;

Die Bohrmaschine wird aufgerufen mit
with Bohrmaschine do begin
an_aus := true;
Bohrertyp := 'spiral';
Drehzahl := 700;
Lochtiefe := 27;
dummy := 0;
Ergebnis := aktivieren(an_aus, Bohrertyp, Drehzahl, Lochtiefe,
dummy);
end;

So müßte es gehen.
Auf eine kurze Erfolgsmeldung freut sich
j/\a
--
Marc Santhoff
2014-04-11 17:37:15 UTC
Permalink
Post by M. Behrendt
Meine Frage ist: Wenn ich in Arbeitsschritt die unter Tmaschine
deklarierte function aktiviere; aufrufe, wie kann ich dann die
Parameter der spezifischen Maschine mitübergeben, die ich z.B. bei
Tbohrmaschine.aktiviere(i1,i2,i3,o1) brauche? Bei bohrmaschine muss
ich drei Inputs mitübergeben (Bohrertyp, Drehzahl, Lochtiefe), bei
einer Anzeige nur einen Input (Anzeigewert, logisch).
Ich schätze Du willst den Fall behandeln, daß die Bohrmaschine
selbsttätig ihr Werkzeug wechselt? Eine Tischbohrmaschine bekommt den
nämlich erstmal eingespannt, bevor sie zum Aktivieren bereit ist.
Post by M. Behrendt
Aber wie bekomme ich die unterschiedliche Zahl und Werte der
Parameter in die allgemeinere function aktiviere; des Typs Tmaschine
um ihn unter arbeitsschritt aufzurufen? Selbst mit einem override
über die Tmaschine.aktiviere krieg ich doch die Parameter nicht
dorthin.
Du kannst beides zusammen erledigen, indem Du eine Art
AKtivierungsobjekt modellierst.

Etwa:

TMaschine.Aktiviere(data: TActivationData);

TActivationData = class();

TBoMaActivation = class(TActivationData)
Bohrer: ...;
Drehzahl: integer;
Tiefe: single;
end;

Dann wird jede Maschine mit einem für sie abgeleiteten Parameterobjekt
aktiviert.

Marc
M. Behrendt
2014-04-11 19:12:10 UTC
Permalink
oh man,
mindestens 5 unterschiedliche Lösungsansätze an einem Tag

Vielen Dank für die schnellen Antworten.

Ehrlich gesagt bin ich jetzt reichlich erschlagen.
Bei Herrn Thumullas Antwort ist mein Verständnis im zweiten Abschnitt ausgestiegen wohingegen Herr Diettrich mir sogar fundierte C/C++-Kentnisse unterstellt. Jedoch erscheinen mir seine beiden Lösungen relativ einfach und passend. Fast so genial-einfach wie die von Herrn Achterndieck (da hätte ich auch selbst drauf kommen müssen). Die von Herrn Santhoff (woher kenne ich den Namen???) hab ich noch nicht ganz verstanden.

Alles in Allem muss ich mich da jetzt erstmal durchwurschteln und die Ansätze einzeln verstehen, geht es hier doch augenscheinlich um einen wichtigen Schlüsselteil im Projekt.

Vielen vielen Dank für die Hinweise!
Mike.
Marc Santhoff
2014-04-11 19:32:52 UTC
Permalink
Post by M. Behrendt
oh man,
mindestens 5 unterschiedliche Lösungsansätze an einem Tag
Vielen Dank für die schnellen Antworten.
Die von Herrn Santhoff (woher kenne ich den Namen???) hab
ich noch nicht ganz verstanden.
Alles in Allem muss ich mich da jetzt erstmal durchwurschteln und die
Ansätze einzeln verstehen, geht es hier doch augenscheinlich um einen
wichtigen Schlüsselteil im Projekt.
M.Behrend kommt mir auch bekannt vor, OpenOffice.org-Umfeld?

Was verstehts Du nicht?

Die Aktivierungsdaten-Klasse ist ein universeller Parameter für die
Aktivierungsmethode. Die AKtivierungsmethode bekommt nur eine
Objektreferenz (aka Zeiger) und das aktivierte Objekt nimmt sich
daraus, was es braucht. Den Typ sollte es ggf. prüfen, eine Bohrmaschine
kann mit den Aktivierungsparametern eines Förderbandes natürlich nix
anfangen.

Bohrdaten = TBoMaActivation.create;
with Bohrdaten do begin
Bohrere := Nummer4;
Drehzahl := 1200;
Tiefe := 0,27;
end

maschinen_liste[maschinen_durchzaehler].aktiviere(Bohrdaten);

Schalter5 := TSchalterActivation.create;
Schalter5.On := TRUE;

maschinen_liste[maschinen_durchzaehler].aktiviere(Schalter5);

...


HTH,
Marc
Carsten Thumulla
2014-04-12 07:11:19 UTC
Permalink
Post by M. Behrendt
Bei Herrn Thumullas Antwort ist mein Verständnis im zweiten Abschnitt ausgestiegen
Die Taktstraße besteht aus Maschinen und aus einer Förderstrecke. Ich
dachte, das sei das Problem.
Jede Maschine braucht dann eine Kommunikation mit der Nachbarmaschine
für die Förderstrecke. Dann gibt es noch Takte innerhalb der Maschine.
Diese beiden haben nur gemeinsam, Werkstück angeliefert und Werkstück
fertig.


ct
M. Behrendt
2014-04-29 19:36:50 UTC
Permalink
Hallo mal wieder!

Wie sicherlich bemerkt wurde, kam ich wegen Ostern noch nicht dazu, mich mit dem Problem weiter zu befassen.
Hab es mir heute wieder vorgenommen.

Eine Frage vorab:
Ich habe ja grob skizziert:

TmyObject:Tobject;
object_Art:word;
...
function tu_was:boolean;
end;

Tmein_abgeleitetes_Object:TmyObject;
...
function tu_was:boolean; (muss hier ein "override" rein?)
end;

var Objektliste:array[0..x] of TmyObject;

implementation
function myObject.tu_was
begin
//leer
end;

function mein_abgeleitetes_Object.tu_was;
begin
prüfe_mschinenzustand;
aktiviere_arbeitsvorgang;
prüfe_werkstück;
prüfe_maschine;
result:=true;
end;

Kann/Wie kann ich per Durchlauf mittels
Objektliste[Zähler].tu_was
tatsächlich die Methode tu_was von mein_abgeleitetes_Object und nicht die Methode tu_was von TmyObject aufrufen?

Wenn das ginge, könnte ich mir die Sache mit der zentralen Übergabe der spezifischen Parameter der einzelnen Maschine/Werkstück-Kombination sparen.
Dann würde ich die
function mein_abgeleitetes_Object.tu_was
begin

um einen Aufruf einer weiteren function/procedure erweitern, und zwar vor allen anderen um
hole_eingangsparameter;

und dann normal weiter wie oben
prüfe_mschinenzustand;
aktiviere_arbeitsvorgang;
prüfe_werkstück;
prüfe_maschine;
result:=true;
end;

Denn die Anzahl und Art der Eingangsparameter ist ja je nach Maschinentyp object_Art (Schalter, Signalleuchte, Bohrmaschine, Schleifmaschine, Fräse, usw.) unterschiedlich.

Also, kurz:
Wie gelingt der Aufruf einer Methode eines abgeleiteten Objects bei Durchlauf der Liste von myObject?

Wahrscheinlich denke ich einfach nur wieder zu kompliziert, (Kann ich gut, kommt nur nie was bei rum...)
Hans-Peter Diettrich
2014-04-29 22:46:26 UTC
Permalink
Post by M. Behrendt
TmyObject:Tobject;
object_Art:word;
....
function tu_was:boolean;
virtual;
Post by M. Behrendt
end;
Tmein_abgeleitetes_Object:TmyObject;
....
function tu_was:boolean; (muss hier ein "override" rein?)
override; //ja
Post by M. Behrendt
end;
var Objektliste:array[0..x] of TmyObject;
implementation
function myObject.tu_was
begin
//leer
Result := False; //Bei Funktionen immer ein Ergebnis zuweisen!
Post by M. Behrendt
end;
DoDi
Marc Santhoff
2014-04-30 06:18:20 UTC
Permalink
Post by Hans-Peter Diettrich
Post by M. Behrendt
TmyObject:Tobject;
object_Art:word;
....
function tu_was:boolean;
virtual;
abstract;
Post by Hans-Peter Diettrich
Post by M. Behrendt
end;
Bewirkt, daß der Compiler prüft, ob die Methode auch wirklich
in jedem abgeleiteten Objekttyp (=Klasse) überschrieben wurde. Wenn
nicht wirft er einen Fehler.

Man könnte auch in TmyObject eine Laufzeitfehlermeldung auslösen, aber
die wird eventuell zu einem ungünstigen Zeitpunkt ausgelöst und
(zer)stört womöglich die Maschine.

Gilt natürlich nur, wenn nicht in TmyObject.tu_was irgendetwas
sinnvolles erledigt werden muß, daß für alle abgeleiteten Klassen nötig
ist.

Marc
Post by Hans-Peter Diettrich
Post by M. Behrendt
Tmein_abgeleitetes_Object:TmyObject;
....
function tu_was:boolean; (muss hier ein "override" rein?)
override; //ja
Post by M. Behrendt
end;
var Objektliste:array[0..x] of TmyObject;
implementation
function myObject.tu_was
begin
//leer
Result := False; //Bei Funktionen immer ein Ergebnis zuweisen!
Post by M. Behrendt
end;
DoDi
Hans-Peter Diettrich
2014-04-30 11:06:29 UTC
Permalink
Post by Marc Santhoff
Post by Hans-Peter Diettrich
Post by M. Behrendt
TmyObject:Tobject;
object_Art:word;
....
function tu_was:boolean;
virtual;
abstract;
Post by Hans-Peter Diettrich
Post by M. Behrendt
end;
Bewirkt, daß der Compiler prüft, ob die Methode auch wirklich
in jedem abgeleiteten Objekttyp (=Klasse) überschrieben wurde. Wenn
nicht wirft er einen Fehler.
Man könnte auch in TmyObject eine Laufzeitfehlermeldung auslösen, aber
die wird eventuell zu einem ungünstigen Zeitpunkt ausgelöst und
(zer)stört womöglich die Maschine.
Gilt natürlich nur, wenn nicht in TmyObject.tu_was irgendetwas
sinnvolles erledigt werden muß, daß für alle abgeleiteten Klassen nötig
ist.
*Abstrakte* Methoden sind die beste Möglichkeit zur Erzeugung
*unerwarteter* Exceptions, die dann wegen unzureichender Behandlung
tatsächlich Schaden anrichten können. Es wäre weit sicherer, wenn eine
Methode, die *unbedingt* überschrieben werden muß, bei ihrem
unwerwarteten Aufruf direkt eine ordentliche Notabschaltung durchführt,
bis der Fehler in der Software behoben ist. Zur Behebung des Fehlers hat
es sich bewährt, in so einer Methode alle Informationen auszugeben
(loggen), die zur schnellen Behebung des Fehlers notwendig sind. Ein
geringer Aufwand, der im Fall des Falles viel Zeit und damit Geld spart.

Zudem sollte eine Notabschaltung *realer* Maschinen immer funktionieren,
und von Anfang an implementiert sein. Details können später nachgetragen
werden, damit jede Maschine in jedem Betriebszustand mit minimalem
Schadensrisiko heruntergefahren wird. Selbst bei einer reinen Simulation
sollte ein Programm nicht einfach abstürzen, sondern vorher noch
brauchbare Informationen zur Fehlerursache und Beseitigung ausgeben
(s.o.), für eine anschließende post-mortem Analyse.

DoDi
Marc Santhoff
2014-04-30 12:45:55 UTC
Permalink
Post by Hans-Peter Diettrich
Post by Marc Santhoff
Post by Hans-Peter Diettrich
Post by M. Behrendt
TmyObject:Tobject;
object_Art:word;
....
function tu_was:boolean;
virtual;
abstract;
Post by Hans-Peter Diettrich
Post by M. Behrendt
end;
Bewirkt, daß der Compiler prüft, ob die Methode auch wirklich
in jedem abgeleiteten Objekttyp (=Klasse) überschrieben wurde. Wenn
nicht wirft er einen Fehler.
Man könnte auch in TmyObject eine Laufzeitfehlermeldung auslösen,
aber die wird eventuell zu einem ungünstigen Zeitpunkt ausgelöst und
(zer)stört womöglich die Maschine.
Gilt natürlich nur, wenn nicht in TmyObject.tu_was irgendetwas
sinnvolles erledigt werden muß, daß für alle abgeleiteten Klassen
nötig ist.
*Abstrakte* Methoden sind die beste Möglichkeit zur Erzeugung
*unerwarteter* Exceptions, die dann wegen unzureichender Behandlung
tatsächlich Schaden anrichten können.
Ähm ... lange nicht konkret gemacht: schweigt der Compiler dazu oder
hast Du ihn so eingestellt? Ich war davon ausgegangen, daß der Fehler
zum Abbruch beim kompilieren führt. Das läßt sich doch per statischer
Typprüfung feststellen.
Post by Hans-Peter Diettrich
Es wäre weit sicherer, wenn
eine Methode, die *unbedingt* überschrieben werden muß, bei ihrem
unwerwarteten Aufruf direkt eine ordentliche Notabschaltung
durchführt, bis der Fehler in der Software behoben ist. Zur Behebung
des Fehlers hat es sich bewährt, in so einer Methode alle
Informationen auszugeben (loggen), die zur schnellen Behebung des
Fehlers notwendig sind. Ein geringer Aufwand, der im Fall des Falles
viel Zeit und damit Geld spart.
Zudem sollte eine Notabschaltung *realer* Maschinen immer
funktionieren, und von Anfang an implementiert sein. Details können
später nachgetragen werden, damit jede Maschine in jedem
Betriebszustand mit minimalem Schadensrisiko heruntergefahren wird.
Selbst bei einer reinen Simulation sollte ein Programm nicht einfach
abstürzen, sondern vorher noch brauchbare Informationen zur
Fehlerursache und Beseitigung ausgeben (s.o.), für eine anschließende
post-mortem Analyse.
Ist klar, das war das Ziel.

Marc
Marc Santhoff
2014-04-30 13:13:03 UTC
Permalink
Post by Marc Santhoff
Post by Hans-Peter Diettrich
*Abstrakte* Methoden sind die beste Möglichkeit zur Erzeugung
*unerwarteter* Exceptions, die dann wegen unzureichender Behandlung
tatsächlich Schaden anrichten können.
Ähm ... lange nicht konkret gemacht: schweigt der Compiler dazu oder
hast Du ihn so eingestellt? Ich war davon ausgegangen, daß der Fehler
zum Abbruch beim kompilieren führt. Das läßt sich doch per statischer
Typprüfung feststellen.
Böse Falle, das schluckt der tatsächlich.

Ich nehme alles zurück und behaupte das Gegenteil ...

Marc
Arno Garrels
2014-04-30 17:02:15 UTC
Permalink
Post by Marc Santhoff
Post by Marc Santhoff
Post by Hans-Peter Diettrich
*Abstrakte* Methoden sind die beste Möglichkeit zur Erzeugung
*unerwarteter* Exceptions, die dann wegen unzureichender Behandlung
tatsächlich Schaden anrichten können.
Ähm ... lange nicht konkret gemacht: schweigt der Compiler dazu oder
hast Du ihn so eingestellt? Ich war davon ausgegangen, daß der Fehler
zum Abbruch beim kompilieren führt. Das läßt sich doch per statischer
Typprüfung feststellen.
Böse Falle, das schluckt der tatsächlich.
Er gibt standardmässig eine Warnung aus wenn der constructor augerufen wird:
[dcc32 Warning] Unit1.pas(34): W1020 Constructing instance of 'TMyClass' containing abstract method 'TMyClass.TuWas'
Post by Marc Santhoff
Ich nehme alles zurück und behaupte das Gegenteil ...
Man kann natürlich der IDE bebringen diese Compilerwarnung als
Fehler zu behandeln.

Manche Leute lassen sämtliche Warnungen als Fehler behandeln, dann
werden die wenigstens aufgelöst.
--
Arno
Arno Garrels
2014-04-30 17:06:57 UTC
Permalink
Post by Arno Garrels
Post by Marc Santhoff
Post by Marc Santhoff
Post by Hans-Peter Diettrich
*Abstrakte* Methoden sind die beste Möglichkeit zur Erzeugung
*unerwarteter* Exceptions, die dann wegen unzureichender Behandlung
tatsächlich Schaden anrichten können.
Ähm ... lange nicht konkret gemacht: schweigt der Compiler dazu oder
hast Du ihn so eingestellt? Ich war davon ausgegangen, daß der
Fehler zum Abbruch beim kompilieren führt. Das läßt sich doch per
statischer Typprüfung feststellen.
Böse Falle, das schluckt der tatsächlich.
Er gibt standardmässig eine Warnung aus wenn der constructor
standardmäßig
aufgerufen

Legastheniker und zwei Kurzschuljahre :)
Arno Garrels
2014-04-30 17:39:18 UTC
Permalink
Post by Marc Santhoff
Post by Marc Santhoff
Post by Hans-Peter Diettrich
*Abstrakte* Methoden sind die beste Möglichkeit zur Erzeugung
*unerwarteter* Exceptions, die dann wegen unzureichender Behandlung
tatsächlich Schaden anrichten können.
Ähm ... lange nicht konkret gemacht: schweigt der Compiler dazu oder
hast Du ihn so eingestellt? Ich war davon ausgegangen, daß der Fehler
zum Abbruch beim kompilieren führt. Das läßt sich doch per statischer
Typprüfung feststellen.
Böse Falle, das schluckt der tatsächlich.
Er gibt standardmässig eine Warnung aus wenn der constructor augerufen wird:
[dcc32 Warning] Unit1.pas(34): W1020 Constructing instance of 'TMyClass' containing abstract method 'TMyClass.TuWas'
Post by Marc Santhoff
Ich nehme alles zurück und behaupte das Gegenteil ...
Arrgh, das bringt's natürlich nicht. Aber anstatt der abstrakten Methode
vieleicht ein Interface benutzen?
--
Arno
Hans-Peter Diettrich
2014-04-30 16:07:42 UTC
Permalink
Post by Marc Santhoff
Post by Hans-Peter Diettrich
Post by Marc Santhoff
Post by Hans-Peter Diettrich
Post by M. Behrendt
function tu_was:boolean;
virtual;
abstract;
Post by Hans-Peter Diettrich
Post by M. Behrendt
end;
Bewirkt, daß der Compiler prüft, ob die Methode auch wirklich
in jedem abgeleiteten Objekttyp (=Klasse) überschrieben wurde. Wenn
nicht wirft er einen Fehler.
Das hatte ich übersehen, das stimmt natürlich nicht.
Post by Marc Santhoff
Post by Hans-Peter Diettrich
*Abstrakte* Methoden sind die beste Möglichkeit zur Erzeugung
*unerwarteter* Exceptions, die dann wegen unzureichender Behandlung
tatsächlich Schaden anrichten können.
Ähm ... lange nicht konkret gemacht: schweigt der Compiler dazu oder
hast Du ihn so eingestellt? Ich war davon ausgegangen, daß der Fehler
zum Abbruch beim kompilieren führt. Das läßt sich doch per statischer
Typprüfung feststellen.
Was soll eine statische Typprüfung hier bewirken?

Es kann ja durchaus Absicht sein, wenn eine abstrakte Mehode nicht
gleich in der ersten abgeleiteten Klasse überschrieben wird, sondern
erst in einer noch weiter entfernten.

Tatsächlich wird jede abstrakte Methode auf eine Methode umgebogen,
welche beim Aufruf eine Exception wirft.

DoDi
Marc Santhoff
2014-04-30 16:55:42 UTC
Permalink
Post by Hans-Peter Diettrich
Post by Marc Santhoff
Post by Hans-Peter Diettrich
*Abstrakte* Methoden sind die beste Möglichkeit zur Erzeugung
*unerwarteter* Exceptions, die dann wegen unzureichender
Behandlung tatsächlich Schaden anrichten können.
Ähm ... lange nicht konkret gemacht: schweigt der Compiler dazu oder
hast Du ihn so eingestellt? Ich war davon ausgegangen, daß der
Fehler zum Abbruch beim kompilieren führt. Das läßt sich doch per
statischer Typprüfung feststellen.
Was soll eine statische Typprüfung hier bewirken?
Es kann ja durchaus Absicht sein, wenn eine abstrakte Mehode nicht
gleich in der ersten abgeleiteten Klasse überschrieben wird, sondern
erst in einer noch weiter entfernten.
Tatsächlich wird jede abstrakte Methode auf eine Methode umgebogen,
welche beim Aufruf eine Exception wirft.
Es ist durchaus möglich festzustellen, ob die VMT nach dem Übersetzen
Zeiger auf abstrakte Methoden enthält, denke ich. Die Frage ist, ob der
Compiler es tut. Außer natürlich diese Aufgabe wird vom Runtime-Linker
erst ausgeführt, dann geht's nicht.

Allerdings kenne ich die Interna des Compilers nicht gut genug, um das
sicher zu wissen. Ist das so?

Oder gibt es nicht wenigstens einen Compiler-Schalter, um diese Prüfung
zu erzwingen?

Marc
M. Behrendt
2014-05-03 12:19:57 UTC
Permalink
Hallo!

Weiter im Text: ;)

Ich habe nun also
TmyObject:Tobject;
object_Art:word;
public
...
procedure tu_was; virtual; abstract; (function->procedure geändert)
...
end;

und weiter

Tmein_abgeleitetes_Object:TmyObject;
...
public
procedure tu_was; override;
end;

implementation

procedure Tmein_abgeleitetes_Object.tu_was;
begin
...
end;

Soweit erstmal.

Jetzt rufe ich in der Procedure Arbeitschritt die procedure tu_was auf (Tmein_abgeleitetes_Object war ja zwischenzeitlich nur eine Verallgemeinerung):

procedure arbeitsschritt;
var maschinen_durchzaehler:word;
begin
maschinen_durchzaehler:=0;
repeat
if maschinen_liste[maschinen_durchzaehler].maschinen_type=10 then
begin
maschinen_liste[maschinen_durchzaehler].bereit:=false;
maschinen_liste[maschinen_durchzaehler].tu_was;
end;
inc(maschinen_durchzaehler);
until maschinen_durchzaehler=length(maschinen_liste);
end;

an der Stelle maschinen_liste[maschinen_durchzaehler].tu_was;
wird ein Laufzeitfehler TAbstractError ausgeworfen
Eine Definition von Tmy_Objekt.tu_was wird seitens des Compilers nicht erlaubt.
([Pascal Fehler] UMasch1.pas(127): E2136 Definition für abstrakte Methode 'tu_was' nicht erlaubt)

Trotzdem wird zur Laufzeit auf die Tmy_Objekt.tu_was anstatt auf die Tmein_abgeleitetes_Object.tu_was zugegriffen?
(So verstehe ich den TAbstractError.)
Hans-Peter Diettrich
2014-05-03 13:21:42 UTC
Permalink
Post by M. Behrendt
procedure arbeitsschritt;
var maschinen_durchzaehler:word;
Wieso "word"? Das war mal in 16 Bit Programmen ein brauchbarer Datentyp,
heute eher nicht mehr. Wenn es (wie hier) auf die exakte Größe des
Datentyps nicht ankommt, empfiehlt sich Integer bzw. Cardinal.
Post by M. Behrendt
begin
maschinen_durchzaehler:=0;
repeat
if maschinen_liste[maschinen_durchzaehler].maschinen_type=10 then
begin
maschinen_liste[maschinen_durchzaehler].bereit:=false;
maschinen_liste[maschinen_durchzaehler].tu_was;
end;
inc(maschinen_durchzaehler);
until maschinen_durchzaehler=length(maschinen_liste);
end;
Wenn die Liste leer ist, kann das "repeat" Ärger geben. Wenn Du Dir
Delphi Beispielcode schon mal angeschaut hast (RTL, Samples...), dann
wird stattdessen meist "for" verwendet, das ist einfacher, schneller und
sicherer.
Post by M. Behrendt
an der Stelle maschinen_liste[maschinen_durchzaehler]..tu_was;
wird ein Laufzeitfehler TAbstractError ausgeworfen
Was für ein Objekt hast Du denn in Deine Liste eingefügt?
Post by M. Behrendt
Eine Definition von Tmy_Objekt.tu_was wird seitens des Compilers nicht erlaubt.
([Pascal Fehler] UMasch1.pas(127): E2136 Definition für abstrakte Methode 'tu_was' nicht erlaubt)
Klar, das ist ja der Sinn von "abstract".
Post by M. Behrendt
Trotzdem wird zur Laufzeit auf die Tmy_Objekt.tu_was anstatt auf die Tmein_abgeleitetes_Object.tu_was zugegriffen?
(So verstehe ich den TAbstractError.)
Wieso *trotzdem*?

Dein Objekt ist offensichtlich vom Typ TmyObject, und bei dessen
Erzeugung solltest Du eine Warnung erhalten haben, daß das Objekt
abstrakte Methoden enthält - spicht Du solltest einen brauchbaren Typ
(abgeleitete Klasse) verwenden. Du solltest die Hints und Warnungen des
Compilers ernst nehmen, und erst mal alle Fehler beseitigen, bevor Du
Dein Programm laufen läßt.

DoDi
M. Behrendt
2014-05-03 13:53:41 UTC
Permalink
Post by Hans-Peter Diettrich
Post by M. Behrendt
procedure arbeitsschritt;
var maschinen_durchzaehler:word;
Wieso "word"?
Weil
a) ich keine negative Anzahl von Dingen kenne,
b) ich keine 2Millionen Maschinen habe und
c) Word als Datentyp unter Delphi 7 marginal (4%) schneller abgearbeitet wird als Integer, wobei Cardinal nochmal 7% schneller läuft als Word (insofern haben sie recht) (Quelle? meine eigene: http://www.mikebeh.de/datacomp.htm ; dazu habe ich eine weitere, unveröffentliche Auswertung, in der die Datentypen untereinander verglichen werden)
Post by Hans-Peter Diettrich
wird stattdessen meist "for" verwendet, das ist einfacher, schneller und
Einspruch, ebenfalls selbst for Jahren getestet
repeat
..
until

i.V.m. inc()/dec() ist schneller als for .. to
(zumindest unter Delphi 7)
"einfacher" ist auch hier nur eine Frage der Übung und sicherer eine Frage der vorherigen Wertprüfung
Post by Hans-Peter Diettrich
Post by M. Behrendt
an der Stelle maschinen_liste[maschinen_durchzaehler]..tu_was;
wird ein Laufzeitfehler TAbstractError ausgeworfen
Was f�r ein Objekt hast Du denn in Deine Liste eingef�gt?
Zur Laufzeit stehen die beiden Maschinen (also Instanzen von Tmein_abgeleitetes_objekt in der maschinen_liste, das zeigt mir die IDE an diesem Punkt an. Allerdings werden die Inhalte der Liste als "[0]: $A7C6C0" angezeigt, ein Hinweis auf die Zeiger-Eigenschaft???
Post by Hans-Peter Diettrich
Post by M. Behrendt
Trotzdem wird zur Laufzeit auf die Tmy_Objekt.tu_was anstatt auf die Tmein_abgeleitetes_Object.tu_was zugegriffen?
(So verstehe ich den TAbstractError.)
Wieso *trotzdem*?
Weil ich dachte, ich hätte doch nur Tmein_abgeleitetes_Object.tu_was definiert und nicht Tmy_Objekt.tu_was, deswegen sollte er m.E. auch die Methode der abgeleiteten Klasse verwenden.
Frage dazu: Wo muss die Deklaration der procedure tu_was in der Deklaration von Tmy_Objekt stehen: im public-Sektor oder woanders?
Post by Hans-Peter Diettrich
Dein Objekt ist offensichtlich vom Typ TmyObject, und bei dessen
Erzeugung solltest Du eine Warnung erhalten haben, da� das Objekt
Hinweise, Warnungen und Fehler: 0
Alle möglichen Punkte in Projekt-Optionen angehakt, nur keine Optimierung, Stack-Frames und Debug-DCU's
M. Behrendt
2014-05-03 14:44:28 UTC
Permalink
Post by M. Behrendt
Post by Hans-Peter Diettrich
Was f�r ein Objekt hast Du denn in Deine Liste eingef�gt?
Zur Laufzeit stehen die beiden Maschinen (also Instanzen von Tmein_abgeleitetes_objekt in der maschinen_liste, das zeigt mir die IDE an diesem Punkt an. Allerdings werden die Inhalte der Liste als "[0]: $A7C6C0" angezeigt, ein Hinweis auf die Zeiger-Eigenschaft???
Die Liste von Maschinen ist übrigens ein global definiertes

var maschinen_liste: array of Tmaschine;

ändert das was?

eine Liste von TBohrmaschine (Tmein_abgeleitetes_Objekt) kann ich ja nicht gebrauchen.
Post by M. Behrendt
Frage dazu: Wo muss die Deklaration der procedure tu_was in der Deklaration von Tmy_Objekt stehen: im public-Sektor oder woanders?
auch wenns nicht im public-Bereich deklariert ist kommt der Fehler
Hans-Peter Diettrich
2014-05-03 17:14:45 UTC
Permalink
Post by M. Behrendt
Die Liste von Maschinen ist übrigens ein global definiertes
var maschinen_liste: array of Tmaschine;
ändert das was?
Nein, der "repeat" Block wirft nur eine Exception, wenn das Array leer ist.
Post by M. Behrendt
Post by M. Behrendt
Frage dazu: Wo muss die Deklaration der procedure tu_was in der
Deklaration von Tmy_Objekt stehen: im public-Sektor oder woanders?
Kommt drauf an. Wenn die Methode von außerhalb des Objekts aufrufbar
sein soll, dann (mindestens) public, sonst protected.
Post by M. Behrendt
auch wenns nicht im public-Bereich deklariert ist kommt der Fehler
Der Compiler meckert schon beim Übersetzen, wenn die Sichtbarkeit nicht
paßt.

DoDi
Hans-Peter Diettrich
2014-05-03 17:14:49 UTC
Permalink
Post by M. Behrendt
"einfacher" ist auch hier nur eine Frage der Übung und sicherer eine
Frage der vorherigen Wertprüfung
...die im vorliegenden Code fehlt.
Post by M. Behrendt
Zur Laufzeit stehen die beiden Maschinen (also Instanzen von
Tmein_abgeleitetes_objekt in der maschinen_liste, das zeigt mir die
IDE an diesem Punkt an.
Das glaube ich nicht :-]
Post by M. Behrendt
Allerdings werden die Inhalte der Liste als
"[0]: $A7C6C0" angezeigt, ein Hinweis auf die Zeiger-Eigenschaft???
Seltsam. Zeig doch mal den Code, der das Maschinen-Array füllt.

DoDi
M. Behrendt
2014-05-04 14:46:56 UTC
Permalink
Post by Hans-Peter Diettrich
"einfacher" ist auch hier nur eine Frage der �bung und sicherer eine
Frage der vorherigen Wertpr�fung
...die im vorliegenden Code fehlt.
touché,
aber in diesem Falle leicht überschaubar
Post by Hans-Peter Diettrich
Das glaube ich nicht :-]
ist aber so
aber da brachten Sie mich auf eine Idee
Der constructor create vom Tmy_Objekt wird ja standardmäßig nicht vom Editor dargestellt.

Was passiert, wenn ich diesen auf

constructor create; virtual; abstract;

und den des abgeleiteten

Tmein_abegeleitetes_Objekt auf
constructor create; override;

setze?

Gute Idee, dachte ich, bringt aber nix. Ich dachte ein create von Tmein_abgeleitetes_Objekt wird auf Tmy_Objekt.create umgeleitet und damit Ihre Vermutung bestätigt.

Trotzdem gleicher Fehler.
Carsten Thumulla
2014-05-03 14:07:59 UTC
Permalink
Post by Hans-Peter Diettrich
Post by M. Behrendt
procedure arbeitsschritt;
var maschinen_durchzaehler:word;
Wieso "word"? Das war mal in 16 Bit Programmen ein brauchbarer Datentyp,
heute eher nicht mehr. Wenn es (wie hier) auf die exakte Größe des
Datentyps nicht ankommt, empfiehlt sich Integer bzw. Cardinal.
klar, für negative Maschinenschritte
M. Behrendt
2014-05-04 21:14:07 UTC
Permalink
Da in dem Thread inzwischen rechtes Durcheinander herrscht, nochmal im Klartext von vorn:

Deklariert wurden
type Tmaschine=class
public
maschine_nummer:word;
maschine_name:string;
maschine_type:byte;
anzahl_inputs:byte;
anzahl_outputs:byte;
inputliste:array of Tio_put;
outputliste:array of Tio_put;
constructor create; virtual; abstract;
function grab_input:boolean; virtual; abstract;
procedure tu_was; virtual; abstract;
var bereit:boolean;
end;

type TSignalleuchte=class(Tmaschine)
const maschine_type=1;
public
var o:Tio_put;
//function tu_was(I_1,I_2:tri):tri;
function grab_input:boolean; override;
constructor create; override;
end;

type TSchalter=class(Tmaschine)
const maschine_type=0;
public
//i1,i2,o:Tio_put;
//function tu_was(I_1,I_2:tri):tri;
constructor create; override;
end;
----------------------------------------------
globale Variablen
var
maschinen_liste:array of Tmaschine;
maschinen_zaehler:word;

und dann

implementation

constructor TSignalleuchte.create;
begin
anzahl_inputs:=1;
anzahl_outputs:=0;
setlength(inputliste,1);
end;

constructor TSchalter.create;
begin
anzahl_inputs:=0;
anzahl_outputs:=1;
end;

function TSignalleuchte.grab_input:boolean;
begin
maschinen_liste[0].inputliste[0].wert:=maschinen_liste[0].inputliste[0].connection.wert;
result:=true;
end;
--------------------------------------------------------------------------------

in einer anderen unit (mit entsprechender uses ...;) wird eine neue Maschine über einen Menüeintrag generiert
Inc(maschinen_zaehler);
maschine_neu(maschinen_zaehler,'maschine_'+ColoredRadioGroup.Name,TSchalter.maschine_type);
coloredradiogroup.maschinenummer:=maschinen_zaehler;
maschinen_liste[coloredradiogroup.maschinenummer].outputliste[0].wert:=coloredradiogroup.outputwert;
maschinen_liste[coloredradiogroup.maschinenummer].bereit:=true;

wobei vorher definiert wurde

procedure TMainFrm.maschine_neu(nummer: Word;maschine_name:string;maschine_type:byte);
var i:word;
begin
setlength(maschinen_liste,maschinen_zaehler);
case maschine_type of
0: maschinen_liste[maschinen_zaehler-1]:=TSchalter.Create;
1: maschinen_liste[maschinen_zaehler-1]:=TSignalleuchte.Create;
10: maschinen_liste[maschinen_zaehler-1]:=TBohrmaschine.Create;
11: maschinen_liste[maschinen_zaehler-1]:=T3Oder.Create;
3: maschinen_liste[maschinen_zaehler-1]:=T3Null_invert.Create;
4: maschinen_liste[maschinen_zaehler-1]:=T3Null_plus.Create;
5: maschinen_liste[maschinen_zaehler-1]:=T3Null_minus.Create;
end;
maschinen_liste[maschinen_zaehler-1].maschine_nummer:=maschinen_zaehler;
maschinen_liste[maschinen_zaehler-1].maschine_name:=maschine_name;
maschinen_liste[maschinen_zaehler-1].maschine_type :=maschine_type;
maschinen_liste[maschinen_zaehler-1].bereit:=false; //Takt vorerst sperren
if maschinen_liste[maschinen_zaehler-1].anzahl_inputs>0 then
begin
setlength(maschinen_liste[maschinen_zaehler-1].inputliste,maschinen_liste[maschinen_zaehler-1].anzahl_inputs);
i:=0;
repeat
maschinen_liste[maschinen_zaehler-1].inputliste[i].nummer:=i;
maschinen_liste[maschinen_zaehler-1].inputliste[i].name:='Input_'+inttostr(i);
inc(i);
until i>maschinen_liste[maschinen_zaehler-1].anzahl_inputs-1;
end;

if maschinen_liste[maschinen_zaehler-1].anzahl_outputs>0 then
begin
setlength(maschinen_liste[maschinen_zaehler-1].outputliste,maschinen_liste[maschinen_zaehler-1].anzahl_outputs);
i:=0;
repeat
maschinen_liste[maschinen_zaehler-1].outputliste[i].nummer:=i;
maschinen_liste[maschinen_zaehler-1].outputliste[i].name:='Output_'+inttostr(i);
inc(i);
until i>maschinen_liste[maschinen_zaehler-1].anzahl_outputs-1;
end;
aktuelles_maschine:=nummer;
if maschinen_liste[maschinen_zaehler-1].anzahl_inputs>0 then maschine_eingaenge_zuweisen(maschinen_liste[maschinen_zaehler-1]);
if maschinen_liste[maschinen_zaehler-1].anzahl_outputs>0 then maschine_ausgaenge_zuweisen(maschinen_liste[maschinen_zaehler-1]);
end;

--------------------------------------------------------------------------------

hier nun der Arbeitsschritt

procedure Arbeitsschritt;
var maschinen_durchzaehler:word;
erg:boolean;
begin
maschinen_durchzaehler:=0;
repeat
if maschinen_liste[maschinen_durchzaehler].maschine_type<>-1 then
begin
maschinen_liste[maschinen_durchzaehler].bereit:=false;
erg:=maschinen_liste[maschinen_durchzaehler].grab_input;
maschinen_liste[maschinen_durchzaehler].tu_was;
maschinen_liste[maschinen_durchzaehler].bereit:=true;
end;
inc(maschinen_durchzaehler);
until maschinen_durchzaehler=length(maschinen_liste);
end;
und genau bei dem Aufruf der function maschinen_liste[maschinen_durchzaehler].grab_input; kommt der TAbstractError;

----------------------------------------------------------------------------------------
Diesmal habe ich mich an die Herleitung in http://delphi-treff.de/object-pascal/virtuelle-dynamische-und-abstrakte-methoden/ gehalten und trotzdem klappt es nicht

Verschiebe ich z.B. die Deklaration var bereit:boolean; in die Deklaration einer abgeleiteten Klasse, kann ich in Arbeitsschritt nicht darauf zugreifen, bzw. der Compiler meckert schon wegen "undefiniert"
Prüfe ich in Arbeitsschritt per "maschinen_liste[maschinen_durchzaehler] Is ..." auf den Typ, wird immer TMaschine ausgegeben.

Ich hoffe, jemand nimmt sich noch die Zeit, mir mal einen Tip zu geben.
Sieghard Schicktanz
2014-05-04 22:31:22 UTC
Permalink
Hallo M.,
...
Post by M. Behrendt
hier nun der Arbeitsschritt
procedure Arbeitsschritt;
...
Post by M. Behrendt
und genau bei dem Aufruf der function
maschinen_liste[maschinen_durchzaehler].grab_input; kommt der
TAbstractError;
Dann schau' mal, _welche_ Deiner "Maschinen" (die Konstruktion ist ja schon
recht abenteuerlich) diese Methode _nicht_ hat. Die produziert den Fehler.

...
Post by M. Behrendt
Diesmal habe ich mich an die Herleitung in
http://delphi-treff.de/object-pascal/virtuelle-dynamische-und-abstrakte-methoden/
gehalten und trotzdem klappt es nicht
Nee, kannste garnicht gehalten haben.
Post by M. Behrendt
Verschiebe ich z.B. die Deklaration var bereit:boolean; in die
Hat nix damit zu tun.
--
--
(Weitergabe von Adressdaten, Telefonnummern u.ä. ohne Zustimmung
nicht gestattet, ebenso Zusendung von Werbung oder ähnlichem)
-----------------------------------------------------------
Mit freundlichen Grüßen, S. Schicktanz
-----------------------------------------------------------
Hans-Peter Diettrich
2014-05-05 07:00:53 UTC
Permalink
In Deinem Code herrscht auch leichtes Durcheinander ;-)

Da Du Dich nicht an die Delphi-Konventionen halten willst, fällt (sicher
nicht nur) mir das Lesen Deines Codes sehr schwer. Insbesondere bekomme
ich Kopfweh bei nur-klein Schreibweise mit ellenlangen Namen und
Wiederholungen. Deshalb nur ein paar Kommentare, bevor ich mich ganz
ausklinke.
Post by M. Behrendt
Deklariert wurden
type Tmaschine=class
constructor create; virtual; abstract;
Ein abstrakter Konstruktur kann nur für Ärger sorgen.
Post by M. Behrendt
globale Variablen
var
maschinen_liste:array of Tmaschine;
maschinen_zaehler:word;
Ich würde hier eine Liste (TObjectList) benutzen, bzw. eine abgeleitete
TMaschinenListe für Objekte des Typs TMaschine.
Post by M. Behrendt
procedure TMainFrm.maschine_neu(nummer: Word;maschine_name:string;maschine_type:byte);
Das wäre ein Kandidat für eine Methode meiner Maschinenliste.
Post by M. Behrendt
var i:word;
Maschine: TMaschine;

Die ständige Wiederholung von maschinen_liste[maschinen_zaehler-1]
vernebelt die Sicht auf den tatsächlichen Ablauf. Die Verwendung einer
Hilfsvariablen mit kürzerem Namen hilft da für mehr Übersicht.
Post by M. Behrendt
begin
setlength(maschinen_liste,maschinen_zaehler);
case maschine_type of
0: maschinen_liste[maschinen_zaehler-1]:=TSchalter.Create;
1: maschinen_liste[maschinen_zaehler-1]:=TSignalleuchte.Create;
10: maschinen_liste[maschinen_zaehler-1]:=TBohrmaschine.Create;
11: maschinen_liste[maschinen_zaehler-1]:=T3Oder.Create;
3: maschinen_liste[maschinen_zaehler-1]:=T3Null_invert.Create;
4: maschinen_liste[maschinen_zaehler-1]:=T3Null_plus.Create;
5: maschinen_liste[maschinen_zaehler-1]:=T3Null_minus.Create;
end;
Da fehlt mir ein "else" mit Fehlermeldung, für unerwartete
Maschinentypen. Für den MaschinenTyp würde ich eine Enum verwenden.

Der restliche Code gehört IMO eher in die Konstruktoren, bzw. in die
Maschinenliste. Mir erschließt sich nicht so recht, was da ablaufen
soll, aber das ist ja Dein Bier.

DoDi
M. Behrendt
2014-05-06 20:11:44 UTC
Permalink
Post by Hans-Peter Diettrich
In Deinem Code herrscht auch leichtes Durcheinander ;-)
... f�llt (sicher nicht nur) mir das Lesen Deines Codes sehr schwer.
den gezeigten Code hatte ich aus dem Projekt raus in einen Texteditor kopiert, und zwar in der Reihenfolge Deklaration, Definition und Nutzung
ironischerweise zur besseren Lesbarkeit hier im Forum

Wenn dann das hier schon schlecht lesbar ist, dann sollten Sie erstmal den original-Code sehen ! ;)
Post by Hans-Peter Diettrich
Da Du Dich nicht an die Delphi-Konventionen halten willst,
nun, ich habe Programmieren nur in Grundzügen in der Schule gelernt, das nannte sich damals noch GW-Basic
den Rest bis Pascal/C(++), Glide, OpenGL, OpenMP, OpenMPI, usw. usf. habe ich mir stückchenweise autodidaktisch beigebracht
da noch niemand irgendwelchen Code von mir betrachten/nachbearbeiten musste, fiel die unsaubere Programmierung wohl nie so auf
ich selbst vergebe Namen möglichst nach dem mir bekannten Prinzip der Eindeutigkeit, also auch länger und mit bezeichnendem Namen und, auweia, auch mit Unterstrichen
also mache ich (bis auf das führende T bei Typbezeichnungen) so ziemlich alles verkehrt, wenn ich nach dem Tutorial für Namenskonventionen auf Delphi-Treff gehe

Ich werde mich zukünftig mehr an die Vorgaben halten, versprochen!
Auch wenn ich z.B. nicht ganz einsehe, wieso Leerzeichen statt Tabulator als Einrückung verwendet werden soll.
Post by Hans-Peter Diettrich
Ich w�rde hier eine Liste (TObjectList) benutzen, bzw. eine abgeleitete
TMaschinenListe f�r Objekte des Typs TMaschine.
TObjectList war mir z.B. gar nicht bekannt
werde ich mir mal anshauen, danke für den Tip
und TComponentList sieht auch gut aus

Aber wieso gibt es eine TList, mit der man z.B. records verwaltet, die aber mit pointern arbeitet
während TObjectList ohne diese Pointer(schreibweise) auskommt!
Ist doch Mist! alles wieder umstellen?
Post by Hans-Peter Diettrich
Post by M. Behrendt
11: maschinen_liste[maschinen_zaehler-1]:=T3Oder.Create;
3: maschinen_liste[maschinen_zaehler-1]:=T3Null_invert.Create;
4: maschinen_liste[maschinen_zaehler-1]:=T3Null_plus.Create;
5: maschinen_liste[maschinen_zaehler-1]:=T3Null_minus.Create;
end;
das ist alter Code aus einem Parallelprojekt, den hatte ich eigentlich hier im Forum/Newsgroup bereits gelöscht
Post by Hans-Peter Diettrich
f�r unerwartete Maschinentypen.
Wo soll der herkommen? Ich kenne doch meine fünf "Pappenheimer", da wurschtelt keiner drin rum! (Scherz beiseite, klar, Fehlerabfrage fehlt.)
Post by Hans-Peter Diettrich
Mir erschlie�t sich nicht so recht, was da ablaufen
soll, aber das ist ja Dein Bier.
lol, mir auch nicht immer

Vielen Dank für die Hinweise!

p.s.
für Herrn Schicktanz:
('M.'='Mike')=true
;)

M. Behrendt
2014-05-04 21:17:23 UTC
Permalink
Da in dem Thread inzwischen rechtes Durcheinander herrscht, nochmal im Klartext von vorn:

Deklariert wurden
type Tmaschine=class
public
maschine_nummer:word;
maschine_name:string;
maschine_type:byte;
anzahl_inputs:byte;
anzahl_outputs:byte;
inputliste:array of Tio_put;
outputliste:array of Tio_put;
constructor create; virtual; abstract;
function grab_input:boolean; virtual; abstract;
procedure tu_was; virtual; abstract;
var bereit:boolean;
end;

type TSignalleuchte=class(Tmaschine)
const maschine_type=1;
public
function grab_input:boolean; override;
constructor create; override;
end;

type TSchalter=class(Tmaschine)
const maschine_type=0;
public
constructor create; override;
end;
----------------------------------------------
globale Variablen
var
maschinen_liste:array of Tmaschine;
maschinen_zaehler:word;

und dann

implementation

constructor TSignalleuchte.create;
begin
anzahl_inputs:=1;
anzahl_outputs:=0;
setlength(inputliste,1);
end;

constructor TSchalter.create;
begin
anzahl_inputs:=0;
anzahl_outputs:=1;
end;

function TSignalleuchte.grab_input:boolean;
begin
maschinen_liste[0].inputliste[0].wert:=maschinen_liste[0].inputliste[0].connection.wert;
result:=true;
end;
--------------------------------------------------------------------------------

in einer anderen unit (mit entsprechender uses ...;) wird eine neue Maschine über einen Menüeintrag generiert
Inc(maschinen_zaehler);
maschine_neu(maschinen_zaehler,'maschine_'+ColoredRadioGroup.Name,TSchalter.maschine_type);
coloredradiogroup.maschinenummer:=maschinen_zaehler;
maschinen_liste[coloredradiogroup.maschinenummer].outputliste[0].wert:=coloredradiogroup.outputwert;
maschinen_liste[coloredradiogroup.maschinenummer].bereit:=true;

wobei vorher definiert wurde

procedure TMainFrm.maschine_neu(nummer: Word;maschine_name:string;maschine_type:byte);
var i:word;
begin
setlength(maschinen_liste,maschinen_zaehler);
case maschine_type of
0: maschinen_liste[maschinen_zaehler-1]:=TSchalter.Create;
1: maschinen_liste[maschinen_zaehler-1]:=TSignalleuchte.Create;
10: maschinen_liste[maschinen_zaehler-1]:=TBohrmaschine.Create;
11: maschinen_liste[maschinen_zaehler-1]:=T3Oder.Create;
3: maschinen_liste[maschinen_zaehler-1]:=T3Null_invert.Create;
4: maschinen_liste[maschinen_zaehler-1]:=T3Null_plus.Create;
5: maschinen_liste[maschinen_zaehler-1]:=T3Null_minus.Create;
end;
maschinen_liste[maschinen_zaehler-1].maschine_nummer:=maschinen_zaehler;
maschinen_liste[maschinen_zaehler-1].maschine_name:=maschine_name;
maschinen_liste[maschinen_zaehler-1].maschine_type :=maschine_type;
maschinen_liste[maschinen_zaehler-1].bereit:=false; //Takt vorerst sperren
if maschinen_liste[maschinen_zaehler-1].anzahl_inputs>0 then
begin
setlength(maschinen_liste[maschinen_zaehler-1].inputliste,maschinen_liste[maschinen_zaehler-1].anzahl_inputs);
i:=0;
repeat
maschinen_liste[maschinen_zaehler-1].inputliste[i].nummer:=i;
maschinen_liste[maschinen_zaehler-1].inputliste[i].name:='Input_'+inttostr(i);
inc(i);
until i>maschinen_liste[maschinen_zaehler-1].anzahl_inputs-1;
end;

if maschinen_liste[maschinen_zaehler-1].anzahl_outputs>0 then
begin
setlength(maschinen_liste[maschinen_zaehler-1].outputliste,maschinen_liste[maschinen_zaehler-1].anzahl_outputs);
i:=0;
repeat
maschinen_liste[maschinen_zaehler-1].outputliste[i].nummer:=i;
maschinen_liste[maschinen_zaehler-1].outputliste[i].name:='Output_'+inttostr(i);
inc(i);
until i>maschinen_liste[maschinen_zaehler-1].anzahl_outputs-1;
end;
aktuelles_maschine:=nummer;
if maschinen_liste[maschinen_zaehler-1].anzahl_inputs>0 then maschine_eingaenge_zuweisen(maschinen_liste[maschinen_zaehler-1]);
if maschinen_liste[maschinen_zaehler-1].anzahl_outputs>0 then maschine_ausgaenge_zuweisen(maschinen_liste[maschinen_zaehler-1]);
end;

--------------------------------------------------------------------------------

hier nun der Arbeitsschritt
procedure Arbeitsschritt;
var maschinen_durchzaehler:word;
erg:boolean;
begin
maschinen_durchzaehler:=0;
repeat
if maschinen_liste[maschinen_durchzaehler].maschine_type<>-1 then
begin
maschinen_liste[maschinen_durchzaehler].bereit:=false;
erg:=maschinen_liste[maschinen_durchzaehler].grab_input;
maschinen_liste[maschinen_durchzaehler].tu_was;
maschinen_liste[maschinen_durchzaehler].bereit:=true;
end;
inc(maschinen_durchzaehler);
until maschinen_durchzaehler=length(maschinen_liste);
end;
und genau bei dem Aufruf der function maschinen_liste[maschinen_durchzaehler].grab_input; kommt der TAbstractError;

--------------------------------------------------------------------------------

Diesmal habe ich mich an die Herleitung in http://delphi-treff.de/object-pascal/virtuelle-dynamische-und-abstrakte-methoden/ gehalten und trotzdem klappt es nicht

Verschiebe ich z.B. die Deklaration var bereit:boolean; in die Deklaration einer abgeleiteten Klasse, kann ich in Arbeitsschritt nicht darauf zugreifen, bzw. der Compiler meckert schon wegen "undefiniert"
Prüfe ich in Arbeitsschritt per "maschinen_liste[maschinen_durchzaehler] Is ..." auf den Typ, wird immer TMaschine ausgegeben.

Ich hoffe, jemand nimmt sich noch die Zeit, mir mal einen Tip zu geben.
M. Behrendt
2014-05-04 21:19:36 UTC
Permalink
Da in dem Thread inzwischen rechtes Durcheinander herrscht, nochmal im Klartext von vorn:

Deklariert wurden
type Tmaschine=class
public
maschine_nummer:word;
maschine_name:string;
maschine_type:byte;
anzahl_inputs:byte;
anzahl_outputs:byte;
inputliste:array of Tio_put;
outputliste:array of Tio_put;
constructor create; virtual; abstract;
function grab_input:boolean; virtual; abstract;
procedure tu_was; virtual; abstract;
var bereit:boolean;
end;

type TSignalleuchte=class(Tmaschine)
const maschine_type=1;
public
function grab_input:boolean; override;
constructor create; override;
end;

type TSchalter=class(Tmaschine)
const maschine_type=0;
public
constructor create; override;
end;
----------------------------------------------
globale Variablen
var
maschinen_liste:array of Tmaschine;
maschinen_zaehler:word;

und dann

implementation

constructor TSignalleuchte.create;
begin
anzahl_inputs:=1;
anzahl_outputs:=0;
setlength(inputliste,1);
end;

constructor TSchalter.create;
begin
anzahl_inputs:=0;
anzahl_outputs:=1;
end;

function TSignalleuchte.grab_input:boolean;
begin
maschinen_liste[0].inputliste[0].wert:=maschinen_liste[0].inputliste[0].connection.wert;
result:=true;
end;
----------------------------------------------------------------------------------------

in einer anderen unit (mit entsprechender uses ...;) wird eine neue Maschine über einen Menüeintrag generiert
Inc(maschinen_zaehler);
maschine_neu(maschinen_zaehler,'maschine_'+ColoredRadioGroup.Name,TSchalter.maschine_type);
coloredradiogroup.maschinenummer:=maschinen_zaehler;
maschinen_liste[coloredradiogroup.maschinenummer].outputliste[0].wert:=coloredradiogroup.outputwert;
maschinen_liste[coloredradiogroup.maschinenummer].bereit:=true;

wobei vorher definiert wurde

procedure TMainFrm.maschine_neu(nummer: Word;maschine_name:string;maschine_type:byte);
var i:word;
begin
setlength(maschinen_liste,maschinen_zaehler);
case maschine_type of
0: maschinen_liste[maschinen_zaehler-1]:=TSchalter.Create;
1: maschinen_liste[maschinen_zaehler-1]:=TSignalleuchte.Create;
10: maschinen_liste[maschinen_zaehler-1]:=TBohrmaschine.Create;

end;
maschinen_liste[maschinen_zaehler-1].maschine_nummer:=maschinen_zaehler;
maschinen_liste[maschinen_zaehler-1].maschine_name:=maschine_name;
maschinen_liste[maschinen_zaehler-1].maschine_type :=maschine_type;
maschinen_liste[maschinen_zaehler-1].bereit:=false; //Takt vorerst sperren
if maschinen_liste[maschinen_zaehler-1].anzahl_inputs>0 then
begin
setlength(maschinen_liste[maschinen_zaehler-1].inputliste,maschinen_liste[maschinen_zaehler-1].anzahl_inputs);
i:=0;
repeat
maschinen_liste[maschinen_zaehler-1].inputliste[i].nummer:=i;
maschinen_liste[maschinen_zaehler-1].inputliste[i].name:='Input_'+inttostr(i);
inc(i);
until i>maschinen_liste[maschinen_zaehler-1].anzahl_inputs-1;
end;

if maschinen_liste[maschinen_zaehler-1].anzahl_outputs>0 then
begin
setlength(maschinen_liste[maschinen_zaehler-1].outputliste,maschinen_liste[maschinen_zaehler-1].anzahl_outputs);
i:=0;
repeat
maschinen_liste[maschinen_zaehler-1].outputliste[i].nummer:=i;
maschinen_liste[maschinen_zaehler-1].outputliste[i].name:='Output_'+inttostr(i);
inc(i);
until i>maschinen_liste[maschinen_zaehler-1].anzahl_outputs-1;
end;
aktuelles_maschine:=nummer;
if maschinen_liste[maschinen_zaehler-1].anzahl_inputs>0 then maschine_eingaenge_zuweisen(maschinen_liste[maschinen_zaehler-1]);
if maschinen_liste[maschinen_zaehler-1].anzahl_outputs>0 then maschine_ausgaenge_zuweisen(maschinen_liste[maschinen_zaehler-1]);
end;

----------------------------------------------------------------------------------------

hier nun der Arbeitsschritt
procedure Arbeitsschritt;
var maschinen_durchzaehler:word;
erg:boolean;
begin
maschinen_durchzaehler:=0;
repeat
if maschinen_liste[maschinen_durchzaehler].maschine_type<>-1 then
begin
maschinen_liste[maschinen_durchzaehler].bereit:=false;
erg:=maschinen_liste[maschinen_durchzaehler].grab_input;
maschinen_liste[maschinen_durchzaehler].tu_was;
maschinen_liste[maschinen_durchzaehler].bereit:=true;
end;
inc(maschinen_durchzaehler);
until maschinen_durchzaehler=length(maschinen_liste);
end;
und genau bei dem Aufruf der function maschinen_liste[maschinen_durchzaehler].grab_input; kommt der TAbstractError;

----------------------------------------------------------------------------------------
Diesmal habe ich mich an die Herleitung in http://delphi-treff.de/object-pascal/virtuelle-dynamische-und-abstrakte-methoden/ gehalten und trotzdem klappt es nicht

Verschiebe ich z.B. die Deklaration var bereit:boolean; in die Deklaration einer abgeleiteten Klasse, kann ich in Arbeitsschritt nicht darauf zugreifen, bzw. der Compiler meckert schon wegen "undefiniert"
Prüfe ich in Arbeitsschritt per "maschinen_liste[maschinen_durchzaehler] Is ..." auf den Typ, wird immer TMaschine ausgegeben.

Ich hoffe, jemand nimmt sich noch die Zeit, mir mal einen Tip zu geben.
M. Behrendt
2014-05-04 21:32:16 UTC
Permalink
Ich sehe gerade, dass ich gar keine Instanzen der abgeleiteten Klassen erzeuge sonder nur den entsprechenden constructor create; aufrufe.
(Ich hatte gedacht, create macht das gleich mit aber die OH sagt da was anderes.)

Wie bekomme ich denn jetzt in TMainFrm.maschine_neu eine neue Insatnz der Form
var Schalter1:TSchalter;
hin?
Marc Santhoff
2014-05-04 22:10:42 UTC
Permalink
Post by M. Behrendt
Ich sehe gerade, dass ich gar keine Instanzen der abgeleiteten
Klassen erzeuge sonder nur den entsprechenden constructor create;
aufrufe. (Ich hatte gedacht, create macht das gleich mit aber die OH
sagt da was anderes.)
Wie bekomme ich denn jetzt in TMainFrm.maschine_neu eine neue Insatnz
der Form var Schalter1:TSchalter;
hin?
Indem Du im abgeleiteten Objekt dessen Konstruktor benutzt. Im Klartext
muß die ableitende Klasse ihren eigenen Konstruktor haben, der dann als
erstes den Konstruktor der Elternklasse (die sie überchreibt) aufruft.
So werden vorher benötigte Speicherallokationen, etc. sichergestellt.
Im Überschriebenen Destruktor, wo man ebenfalls belegte Ressourcen
wieder freigibt, wird erst die eigene Arbeit verrichtet und dann erst
als letztes der Destruktor der Basisklasse aufgerufen.

Erst das bewirkt, daß beim Konstruktoraufruf in .maschine_neu auch
wirklich derjenige der abgeleiteten Klasse benutzt wird. Technisch
passiert daß, indem die Methodentabelle erst mit denen der Basisklasse
gefüllt wird und nur für die überschriebenen Methoden deren Funktionen
und Prozeduren eingetragen werden. Dabei werden die ererbten
tatsächlich überschrieben, müssen also vom überschreibenden
Programmierer aufgerufen werden.

Marc


TSchalter = class(Tirgendwas)
public
contructor create; override;
destructor destroy;
...
end;

constructor TSchalter.create;
begin
inherited; // bzw. "inherited create;"
... Variablen initialisieren, etc.
end;

destructor TSchalter.destroy;
begin
... Speicher freigeben, Dateien schließen, usw. ...
inherited destroy;
end;
M. Behrendt
2014-05-04 23:30:08 UTC
Permalink
Post by Marc Santhoff
TSchalter = class(Tirgendwas)
public
contructor create; override;
destructor destroy;
...
end;
man was für kompliziertes Zeugs
Post by Marc Santhoff
constructor TSchalter.create;
begin
inherited; // bzw. "inherited create;"
nur inherited; !

inherited create; schmeisst zur Laufzeit wieder ein TAbsractError beim Aufruf des abgeleiteten create raus

So gehts erstmal, jetzt muss ich einiges an den grab_inputs und den tu_was rumwurschteln, mal sehen, wie es weitergeht.

Vielen Dank!
Marc Santhoff
2014-05-05 00:55:56 UTC
Permalink
Post by M. Behrendt
Post by Marc Santhoff
TSchalter = class(Tirgendwas)
public
contructor create; override;
destructor destroy;
...
end;
man was für kompliziertes Zeugs
Sieht nur so aus, daran gewöhnt man sich schnell.

Entweder die Basisklasse macht alles richtig oder man überschreibt die
Methoden und ergänzt, was man braucht.

Natur funktioniert am Ende genauso, ein menschlicher Embryo sieht in
seiner Entwicklung auch mal aus wie ein Fisch, Echse oder Kleinnager,
dann wird erst das weiterentwickelte (überschriebene) Gen angewandt und
es wird ein kleiner Affe^wHomo Sapiens draus. ;)
Post by M. Behrendt
Post by Marc Santhoff
constructor TSchalter.create;
begin
inherited; // bzw. "inherited create;"
nur inherited; !
Stimmt, mein Fehler.
Post by M. Behrendt
Vielen Dank!
Gern, wenn's hilft.

Marc
Hans-Peter Diettrich
2014-05-05 07:02:23 UTC
Permalink
Post by Marc Santhoff
Post by M. Behrendt
Ich sehe gerade, dass ich gar keine Instanzen der abgeleiteten
Klassen erzeuge sonder nur den entsprechenden constructor create;
aufrufe. (Ich hatte gedacht, create macht das gleich mit aber die OH
sagt da was anderes.)
Wie bekomme ich denn jetzt in TMainFrm.maschine_neu eine neue Insatnz
der Form var Schalter1:TSchalter;
hin?
Indem Du im abgeleiteten Objekt dessen Konstruktor benutzt.
Das ist nicht notwendig, wenn der ererbte Konstruktor garnichts macht
(wie z.B. TObject.Create).
Post by Marc Santhoff
Im Klartext
muß die ableitende Klasse ihren eigenen Konstruktor haben, der dann als
erstes den Konstruktor der Elternklasse (die sie überchreibt) aufruft.
Es gibt Fälle, in denen man den ererbeten Konstruktor nicht gleich zu
Anfang des Konstruktors aufruft, aber das gehört dann zu den höheren
Weihen ;-)
Post by Marc Santhoff
So werden vorher benötigte Speicherallokationen, etc. sichergestellt.
Im Überschriebenen Destruktor, wo man ebenfalls belegte Ressourcen
wieder freigibt, wird erst die eigene Arbeit verrichtet und dann erst
als letztes der Destruktor der Basisklasse aufgerufen.
Der Rest ist ziemlich falsch :-(
Post by Marc Santhoff
Erst das bewirkt, daß beim Konstruktoraufruf in .maschine_neu auch
wirklich derjenige der abgeleiteten Klasse benutzt wird. Technisch
passiert daß, indem die Methodentabelle erst mit denen der Basisklasse
gefüllt wird und nur für die überschriebenen Methoden deren Funktionen
und Prozeduren eingetragen werden. Dabei werden die ererbten
tatsächlich überschrieben, müssen also vom überschreibenden
Programmierer aufgerufen werden.
Das mag für C++ &Co gelten (bottom-up), nicht für Delphi (top-down)!

Ein Konstruktor ist nur eine spezielle Methode, die (vom Compiler) auf
zwei unterschiedliche Weisen gehandhabt wird. Beim Aufruf mit einem
Klassentyp (TMaschine.Create) wird ein neues Objekt dieses Typs erzeugt
und initialisiert, incl. VMT. Erst dann läuft der User-Code in Create
ab. Beim Aufruf mit einem Objekt (MeineMaschine.Create) wird nur der
User-Code im Konstruktor ausgeführt, wie bei einem normalen
Methodenaufruf. Diese Variante wird (notwendigerweise) auch beim Aufruf
des ererbten Konstruktors benutzt, der dann ja garkein neues Objekt
erzeugen darf.

DoDi
Lesen Sie weiter auf narkive:
Loading...